// session->mutex must be locked
static int wait_for_playlist_loaded(php_spotify_playlist *playlist) {
	struct timespec ts;
	int err = 0;
	php_spotify_session *session;

	assert(playlist != NULL);
	session = playlist->session;

	DEBUG_PRINT("wait_for_playlist_loaded start\n");

	// Block for a max of SPOTIFY_TIMEOUT seconds
	clock_gettime(CLOCK_REALTIME, &ts);
	ts.tv_sec += SPOTIFY_TIMEOUT;

	request_lock();

	while(err == 0) {
		DEBUG_PRINT("wait_for_playlist_loaded loop\n");
		int loaded;

		session_lock(session);
		loaded = sp_playlist_is_loaded(playlist->playlist);
		session_unlock(session);

		if (loaded)
			break;

		// Wait until a callback is fired
		err = request_sleep_lock(&ts);
	}

	request_unlock();

	DEBUG_PRINT("wait_for_playlist_loaded end(%d)\n", err);
	return err;
}
bool DoLock(CSocketIOPtr s,size_t client, char *param)
{
	ClientLock lock;
	char *flags, *path,*p;
	unsigned uFlags=0;
	size_t lock_to_wait_for;

	if(LockClientMap[client].state!=lcActive)
	{
		s->printf("001 FAIL Unexpected 'Lock' command\n");
		return false;
	}
	path = strchr(param,'|');
	if(!path)
	{
		s->printf("001 FAIL Lock command expects <flags>|<path>\n");
		return false;
	}
	*(path++)='\0';
	flags = param;
	while((p=lock_strchr(flags,' '))!=NULL)
	{
		char c=*p;
		*p='\0';
		if(!strcmp(flags,"Read"))
			uFlags|=lfRead;
		else if(!strcmp(flags,"Write"))
			uFlags|=lfWrite;
		else if(!strcmp(flags,"Advisory"))
			uFlags|=lfAdvisory;
		else if(!strcmp(flags,"Full"))
			uFlags|=lfFull;
		else
		{
			s->printf("001 FAIL Unknown flag '%s'\n",flags);
			return false;
		}
		if(c) flags=p+1;
		else break;
	}
	if(!(uFlags&lfRead) && !(uFlags&lfWrite))
	{
		s->printf("001 FAIL Must specify read or write\n");
		return false;
	}

	if(strncmp(path,LockClientMap[client].root.c_str(),LockClientMap[client].root.size()))
	{
		DEBUG("(#%d) Lock Fail %s not within %s\n",(int)client,path,LockClientMap[client].root.c_str());
		s->printf("001 FAIL Lock not within repository\n");
		return false;
	}

	if(request_lock(client,path,uFlags,lock_to_wait_for))
	{
		VersionMapType ver;
		size_t newId = ++global_lockId;
		if(((uFlags&lfFull) && (uFlags&lfRead)) || (uFlags&lfWrite))
		{
			TransactionListType::const_iterator i = std::find(TransactionList.begin(), TransactionList.end(), path);
			if(i!=TransactionList.end())
			{
				/* This is where atomicity really 'happens' */
				if(i->owner!=client &&   /* Not us */
				   LockClientMap.find(i->owner)!=LockClientMap.end() && /* Exists */
				   TimeIntersects(LockClientMap[i->owner].starttime,LockClientMap[i->owner].endtime,
					   LockClientMap[client].starttime,LockClientMap[client].endtime)) /* Overlaps us */
				{
					printf("Found version %s:%s\n",i->branch.c_str(),i->oldversion.c_str());
					ver[i->branch]=i->oldversion;

					/* If a transaction is still in progress and a version has been committed,
						we can't allow even an advisory write yet. */
					if(uFlags&lfWrite)
					{
						DEBUG("(#%d) Lock request on %s (%s) (wait for transaction by client %d)\n",(int)client,path,FlagToString(uFlags),(unsigned)i->owner);
						s->printf("002 WAIT Lock busy|%s|%s|%s\n",LockClientMap[i->owner].user.c_str(),LockClientMap[i->owner].client_host.c_str(),path);
						return true;
					}
				}
			}
		}
		DEBUG("(#%d) Lock request on %s (%s) (granted %u)\n",(int)client,path,FlagToString(uFlags),(unsigned)newId);
		s->printf("000 OK Lock granted (%u)\n",(unsigned)newId);
		LockMap[newId].flags=uFlags;
		LockMap[newId].path=path;
		LockMap[newId].length=strlen(path);
		LockMap[newId].owner=client;
		LockMap[newId].versions = ver;
	}
	else
	{
		DEBUG("(#%d) Lock request on %s (%s) (wait for %u)\n",(int)client,path,FlagToString(uFlags),(unsigned)lock_to_wait_for);
		s->printf("002 WAIT Lock busy|%s|%s|%s\n",LockClientMap[LockMap[lock_to_wait_for].owner].user.c_str(),LockClientMap[LockMap[lock_to_wait_for].owner].client_host.c_str(),LockMap[lock_to_wait_for].path.c_str());
	}

	return true;
}