コード例 #1
0
ファイル: packetqueue.cpp プロジェクト: cchatterj/isabel
// -----------------------------------------------------------------------------
// PacketQueue::packetReceived
//
// -----------------------------------------------------------------------------
//
void PacketQueue::packetReceived(const unsigned char *data, int length)
{
    //channel->packetReceived2(data, length);
    //return;

    const RTPHeader *header = reinterpret_cast<const RTPHeader*>(data);
    u16 nseq = header->GetSeqNumber();
    u32 ts = header->GetTimestamp();

    long long int ltsdiff = (long long int)ts - (long long int)lastTs;
    int tsdiff = (int)ltsdiff;
    int nseqdiff = nseq - lastNseq;
/*
    // nseq sequence cicle test
    if ( abs(nseqdiff) > ( USHRT_MAX - MAX_DIFF ) )
    {
        NOTIFY("Vuelta del NSeq ns=%d last=%d\n", nseq, lastNseq);
        if (nseqdiff > 0)
            nseqdiff-= (USHRT_MAX + 1);
        else
            nseqdiff+= (USHRT_MAX + 1);
    }
*/
    if (abs(tsdiff) > MAX_DIFF_TS || abs(nseqdiff) > MAX_DIFF )
    {
        // new flow, process and clean queue
        channel->packetReceived2(data, length);
        lastNseq = nseq;
        lastTs = ts;
        cleanQueue();
    }
    else if (nseqdiff > 1)
    {
        // Jump in nseq, enqueue
        enqueuePacket(data, length, nseq);
        checkQueue();
    }
    else if (nseqdiff == 1)
    {
        // next packet, process
        channel->packetReceived2(data, length);
        lastNseq = nseq;
        lastTs = ts;
        checkQueue();
    }
    else if (nseqdiff < 0)
    {
        // old packet, discard?
        // stats?
    }
    else if (nseqdiff == 0)
    {
        //duplicate packet, process (for stats)?
        //channel->packetReceived2(data, length);
    }
}
コード例 #2
0
ファイル: par_posix.c プロジェクト: huiyingchu/IN4026Lab
/****************************************************************
*
*	Function: arrayCopy
*	Input:	arg *argv	Pointer to Thread Struct
*
*	Output: returns nothing
*
*	Description: Copys the results from the temporary arrays
*	to their respective output arrays
*
*****************************************************************/
void* arrayCopy(void* argv){
	int i;
	int *S,*P,*R, *R_temp, *P_temp;

	args* input = (args*)argv;
	S = input->S;
	P = input->P;
	R = input->R;
	R_temp = input->R_temp;
	P_temp = input->P_temp;

	int status;
	status = checkQueue(input);

	if(status>0){

		for(i=(input->s1); i<(input->e1); i++){
			R[i] = R_temp[i];
			P[i] = P_temp[i];		
		}
		arrayCopy(input);	

	}
	else{	
		/*Wait for all Threads to Finish*/
		pthread_barrier_wait(&init);
	}
}
コード例 #3
0
	void MyClientRecycleThread::run()
	{
		while(!isFini())
		{
			checkQueue();
			if (!taskSet.empty())
			{
				std::set<MySockClientTask *> willdel;
				std::set<MySockClientTask *>::iterator iter = taskSet.begin();
				for (; iter != taskSet.end(); ++iter)
				{
					if ((*iter)->conn())
					{
						mPool->addIoThread(*iter);
						willdel.insert(*iter);
					}
//					MySockClientTaskManager::getInstance().removeTask(*iter);
//					delete *iter;
				}
				for (iter = willdel.begin(); iter != willdel.end(); ++iter)
				{
					taskSet.erase(*iter);
				}
//				taskSet.clear();
			}
			usleep(10*1000);
		}
	}
コード例 #4
0
/*
 *Checks start of action Queue for command, and actions it if all criteria are met
 */
int popToTower()	{
    ActionQueueStructure queue = getQueue(NULL);
    GameProperties Game = getGame(NULL);
    int needed;
    if(queue->start != NULL) {
        needed = calculateCosts(queue->start->command,queue->start->option,queue->start->target);
        switch(queue->start->command)	{
        case cmd_upgrade:
            if (checkQueue(queue, Game,needed)) {
                upgradeTowerStat(queue->start->option,queue->start->target);
                useMemory(Game, needed);
                removeQueueItem();
            }
            break;
        case cmd_mktwr:
            if (checkQueue(queue,Game,needed)) {
                switch(queue->start->option)	{
                case mktwr_int:
                    createTowerTypeFromPositions(queue->start->target,INT_TYPE);
                    break;
                case mktwr_char:
                    createTowerTypeFromPositions(queue->start->target,CHAR_TYPE);
                    break;
                default:
                    fprintf(stderr,"Unrecognised tower type\n");
                    break;
                }
                //createTowerFromPositions(queue->start->target);
                useMemory(Game, needed);
                removeQueueItem();
            }
            break;
        case cmd_aptget:
            if(checkQueue(queue,Game,needed)) {
                unlock_ability(KILL);
                useMemory(Game, needed);
                removeQueueItem();
            }
        default:

            break;
        }
    } else {
        return 0;
    }
    return 1;
}
コード例 #5
0
ファイル: barber_thread.c プロジェクト: sachithgmg/os
void *waitingRoom(void *p)
{
    //take seat
    pthread_mutex_lock(&queue_mutex);
    checkQueue();

    sleep(returnTime);
    waitingRoom(p);
}
コード例 #6
0
	void MyIoThread::run()
	{
		std::set<MySockTask *> taskWillDel;
		while(!isFini())
		{
			checkQueue();
			int retCode = epoll_wait(epfd, &epev[0], taskSet.size()/*常数级*/, 50/*ms*/);
			for (int i = 0; i < retCode; ++i)
			{
				if (epev[i].events&EPOLLIN || epev[i].events&EPOLLPRI)
				{
					MySockTask *task = static_cast<MySockTask*>(epev[i].data.ptr);
					int len = task->rcvBuffer();
					if (len < 0)
					{
						taskWillDel.insert(task);
					}
					else if (len == 0)
					{
						taskWillDel.insert(task);
					}
					int getMsgRet = task->getMsg();
					if (getMsgRet < 0)
					{
						taskWillDel.insert(task);
					}
				}
				else if (epev[i].events&EPOLLOUT)
				{
					MySockTask *task = static_cast<MySockTask*>(epev[i].data.ptr);
					int len = task->syncSendBuf();
					if (len < 0)
					{
						taskWillDel.insert(task);
					}
				}
				else if (epev[i].events&EPOLLERR)
				{
					MySockTask *task = static_cast<MySockTask*>(epev[i].data.ptr);
					taskWillDel.insert(task);
				}
			}
			if (!taskWillDel.empty())
			{
				std::set<MySockTask *>::iterator iter = taskWillDel.begin();
				for (; iter != taskWillDel.end(); ++iter)
				{
					mPool->addRecycleThread(*iter);
					remove(*iter);
				}
				taskWillDel.clear();
			}
			usleep(5000);
		}
	}
コード例 #7
0
ファイル: NodeVersionLoader.cpp プロジェクト: yvt/Merlion
	void NodeVersionLoader::downloadFailed(const std::string& msg)
	{
		auto self = shared_from_this();
		checkThread();
		
		auto& item = currentDownloadItem();
		
		file.reset();
		
		int retryCount;
		{
			std::lock_guard<std::mutex> lock(downloadQueueMutex);
			retryCount = item.retryCount;
		}
		if (retryCount == 0) {
			BOOST_LOG_SEV(log, LogLevel::Error) <<
			"Failed to download.: " << msg;
			
			auto ver = item.version;
			{
				std::lock_guard<std::mutex> lock(downloadQueueMutex);
				downloadQueue.pop_front();
				// note: `item` is a dangling reference now
			}
			
			onVersionDownloadFailed(ver);
			
		} else {
			BOOST_LOG_SEV(log, LogLevel::Warn) <<
			"Failed to download. Retrying.: " << msg;
		}
		
		log.setChannel(std::string("(none)"));
		
		state = State::Waiting;
		timer.expires_from_now(boost::posix_time::seconds(3));
		timer.async_wait([this, self] (const error_code& error) {
			if (error) {
				return;
			}
			if (disposed) {
				BOOST_LOG_SEV(log, LogLevel::Debug) <<
				"Retry timer aborted.: Disposed.";
				return;
			}
			state = State::Idle;
			checkQueue();
		});
		
	}
コード例 #8
0
int main(int argc,char* argv[])
{
	if(argv[2]==NULL)
	{
		printf("Please enter value of num_Empty!!!\n");
		return 1;
	}
	if(argv[1]==NULL)
	{	
		printf("Please enter File Name!!!\n");
		return 1;
	}
	EMPTY=atoi(argv[2]);
	int readInputFile(char *);
	int displaynetwork();	
	int sendMessage(int ,int ,int ,int );
	int enterQueue(struct Message *,int );
	int displayMessage(struct Message *);
	int checkQueue();	
	struct Message* getMessage(int);
	int initializeQueues();
	int broadcastMessage();
	logFile=fopen("stage7_log","w");
	int statusRead=readInputFile(argv[1]);
	if(statusRead==0)
		displaynetwork();	
	printf("\n");
	//creating totalPeers thread
	int i,status;
	for (i=2; i<totalPeers+2; i++)
 		 thr_create(NULL, 0, peer, (void*)i, THR_BOUND, &thr[i]);
	printf("\n");	
	broadcastMessage();
	//wait for all threads to finish
	while (thr_join(0, &tid, (void**)&status)==0)
  		printf("\n[Status of thread Id %d is=%d]", tid,status);

	
	checkQueue();	
	fclose(logFile);
	fclose(fp);
	return 0;
}
コード例 #9
0
/*
 *Pops from front of Queue. : replaced with popToTower()
 */
int popFromQueue(ActionQueueStructure queue, cmdType *cmd, cmdOption *stat, int *target)	{
    GameProperties Game = getGame(NULL);
    int needed = calculateCosts(*cmd,*stat,*target);

    if((queue->start != NULL) && (checkQueue(queue,Game, needed)))	{ //!	testing target, available Memory, cooldown time
        *cmd = queue->start->command;
        *stat = queue->start->option;
        *target = queue->start->target;
        QueueNode tempStart = queue->start;
        queue->start = queue->start->nextNode;
        free(tempStart);
        --(queue->nItems);
        useMemory(Game, needed);	//use memory
        setlastAction(Game);	//activate cooldown timer.

        return 1;
    }
    return 0;
}
コード例 #10
0
	void MyRecycleThread::run()
	{
		while(!isFini())
		{
			checkQueue();
			if (!taskSet.empty())
			{
				std::set<MySockTask *>::iterator iter = taskSet.begin();
				for (; iter != taskSet.end(); ++iter)
				{
					if (NULL != *iter)
					{
						MySockTaskManager::getInstance().removeTask(*iter);
						delete *iter;
					}
				}
				taskSet.clear();
			}
			usleep(10*1000);
		}
	}
コード例 #11
0
ファイル: par_posix.c プロジェクト: huiyingchu/IN4026Lab
/****************************************************************
*
*	Function: arrayInit
*	Input:	arg *argv	Pointer to Thread Struct
*
*	Output: returns nothing
*
*	Description: Initializes the array with the initial distance
*	values. Done in parallel.
*
*****************************************************************/
void* arrayInit(void* argv){
	int i;
	int *S,*P,*R, *R_temp, *P_temp;

	args* input = (args*)argv;
	S = input->S;
	P = input->P;
	R = input->R;
	R_temp = input->R_temp;
	P_temp = input->P_temp;

	int status;
	status = checkQueue(input);

	if(status>0){

		/*Copy Contents into working Array and initalize R*/
		for(i=(input->s1); i<(input->e1); i++){
			P[i] = S[i];
			P_temp[i] = S[i];
			if(S[i] > 0){
				R[i] = 1;
				R_temp[i] = 1;
			}
			else{
				R[i] = 0;
				R_temp[i] = 0;
			}
		}
		arrayInit(input);	

	}
	else{	
		pthread_barrier_wait(&init);
		if(input->id){		
			pthread_exit(NULL);
		}
	}
}
コード例 #12
0
ファイル: NodeVersionLoader.cpp プロジェクト: yvt/Merlion
	void NodeVersionLoader::download(const std::string &version)
	{
		auto self = shared_from_this();
		
		DownloadItem item;
		item.version = version;
		item.retryCount = 3;
		{
			std::lock_guard<std::mutex> lock(downloadQueueMutex);
			for (auto& item: downloadQueue) {
				if (item.version == version) {
					item.retryCount = 3;
					return;
				}
			}
			downloadQueue.emplace_back(std::move(item));
		}
		
		service.post([this, self]() {
			checkQueue();
		});
	}
コード例 #13
0
ファイル: sendqueue.cpp プロジェクト: TonyAlloa/miranda-dev
int SendQueue::ackMessage(TWindowData *dat, WPARAM wParam, LPARAM lParam)
{
	ACKDATA				*ack = (ACKDATA *) lParam;
	DBEVENTINFO			dbei = { 0};
	HANDLE				hNewEvent;
	int					iFound = SendQueue::NR_SENDJOBS, iNextFailed;
	TContainerData *m_pContainer = 0;
	if (dat)
		m_pContainer = dat->pContainer;

	iFound = (int)(LOWORD(wParam));
	//i = (int)(HIWORD(wParam));

	if (m_jobs[iFound].iStatus == SQ_ERROR) {      // received ack for a job which is already in error state...
		if (dat) {                        // window still open
			if (dat->iCurrentQueueError == iFound) {
				dat->iCurrentQueueError = -1;
				showErrorControls(dat, FALSE);
			}
		}
		/*
		 * we must discard this job, because there is no message window open to handle the
		 * error properly. But we display a tray notification to inform the user about the problem.
		 */
		else
			goto inform_and_discard;
	}

	// failed acks are only handled when the window is still open. with no window open, they will be *silently* discarded

	if (ack->result == ACKRESULT_FAILED) {
		if (dat) {
			/*
			 * "hard" errors are handled differently in multisend. There is no option to retry - once failed, they
			 * are discarded and the user is notified with a small log message.
			 */
			if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND))
				SkinPlaySound("SendError");

			TCHAR *szAckMsg = mir_a2t((char *)ack->lParam);
			mir_sntprintf(m_jobs[iFound].szErrorMsg, safe_sizeof(m_jobs[iFound].szErrorMsg),
						 CTranslator::get(CTranslator::GEN_MSG_DELIVERYFAILURE), szAckMsg);
			m_jobs[iFound].iStatus = SQ_ERROR;
			mir_free(szAckMsg);
			KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound);
			if (!(dat->dwFlags & MWF_ERRORSTATE))
				handleError(dat, iFound);
			return 0;
		}
		else {
inform_and_discard:
			_DebugPopup(m_jobs[iFound].hOwner, CTranslator::get(CTranslator::GEN_SQ_DELIVERYFAILEDLATE));
			clearJob(iFound);
			return 0;
		}
	}

	dbei.cbSize = sizeof(dbei);
	dbei.eventType = EVENTTYPE_MESSAGE;
	dbei.flags = DBEF_SENT;
	dbei.szModule = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) m_jobs[iFound].hOwner, 0);
	dbei.timestamp = time(NULL);
	dbei.cbBlob = lstrlenA(m_jobs[iFound].sendBuffer) + 1;

	if (dat)
		dat->cache->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1);
	else {
		CContactCache *c = CContactCache::getContactCache(m_jobs[iFound].hOwner);
		if(c)
			c->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1);
	}

	if (m_jobs[iFound].dwFlags & PREF_UNICODE)
		dbei.cbBlob *= sizeof(TCHAR) + 1;
	if (m_jobs[iFound].dwFlags & PREF_RTL)
		dbei.flags |= DBEF_RTL;
	if (m_jobs[iFound].dwFlags & PREF_UTF)
		dbei.flags |= DBEF_UTF;
	dbei.pBlob = (PBYTE) m_jobs[iFound].sendBuffer;
	hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) m_jobs[iFound].hOwner, (LPARAM) & dbei);

	if (m_pContainer) {
		if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND))
			SkinPlaySound("SendMsg");
	}

	if (dat && (m_jobs[iFound].hOwner == dat->hContact)) {
		if (dat->hDbEventFirst == NULL) {
			dat->hDbEventFirst = hNewEvent;
			SendMessage(dat->hwnd, DM_REMAKELOG, 0, 0);
		}
	}

	m_jobs[iFound].hSendId = NULL;
	m_jobs[iFound].iAcksNeeded--;

	if (m_jobs[iFound].iAcksNeeded == 0) {              // everything sent
		//if (m_jobs[iFound].hOwner != 0 && dat)
		//	EnableSending(dat, TRUE);
		clearJob(iFound);
		if (dat) {
			KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound);
			dat->iOpenJobs--;
		}
		m_currentIndex--;
	}
	if (dat) {
		checkQueue(dat);
		if ((iNextFailed = findNextFailed(dat)) >= 0 && !(dat->dwFlags & MWF_ERRORSTATE))
			handleError(dat, iNextFailed);
		//MAD: close on send mode
		else {
			if (M->GetByte("AutoClose", 0)) {
				if(M->GetByte("adv_AutoClose_2", 0))
					SendMessage(dat->hwnd, WM_CLOSE, 0, 1);
				else
					SendMessage(dat->pContainer->hwnd, WM_CLOSE, 0, 0);
			}
		}
		//MAD_
	}
	return 0;
}
コード例 #14
0
ファイル: par_posix.c プロジェクト: huiyingchu/IN4026Lab
/****************************************************************
*
*	Function: nodeLength
*	Input:	int *S	Pointer to Input Array S
*		int *R	Pointer to Output Array R
*		int n   Size of the Arrays S and R
*
*	Output: returns nothing
*
*	Description: Takes a linked list array S and finds the
*	distance of each node in S to the final node 0. The 
*	distance is saved in the output array R. 
*
*****************************************************************/
void* nodeLength(void* argv){
	int i;
	int *S,*P,*R, *R_temp, *P_temp;

	args* input = (args*)argv;
	S = input->S;
	P = input->P;
	R = input->R;
	R_temp = input->R_temp;
	P_temp = input->P_temp;

	int status;
	status = checkQueue(input);
	
	if(status>0){

		/*Process each node */
		for(i=(input->s1); i<(input->e1); i++){
			if(P[i] > 0){	
				R_temp[i] = R[i]+R[P[i]];			
				P_temp[i] = P[P[i]];
			}	
		}

		/*Check to see if more work is left*/
		nodeLength(input);
	}
	else{	
		/*Wait for other threads to finish step*/
		pthread_barrier_wait(&barrier);

		/*Reset Work Queue so we can start Copying Results Back*/
		if(input->id == 0){ /*Only master touches it*/
			jobs.queue1[0] = 0;
			jobs.queue1[1] = input->n;
		}

		/*Wait for Master to update the Queue*/
		pthread_barrier_wait(&barrier);

		/*Start Copying array back*/		
		arrayCopy(input);

		/*Wait for other threads to finish*/
		pthread_barrier_wait(&barrier);

		/*Finished all steps*/
		if(status==-1){
			if(input->id){		
				pthread_exit(NULL);
			}
		}
		else{
			/*Reset the work queue*/
			if(input->id == 0){ /*Only master touches it*/
				jobs.m -= 1;		
				jobs.queue1[0] = 0;
				jobs.queue1[1] = input->n;
			}
			/*Wait for other threads to finish step*/
			pthread_barrier_wait(&barrier);
			nodeLength(input);
		}
	}

}
コード例 #15
0
ファイル: sendqueue.cpp プロジェクト: ybznek/miranda-ng
int SendQueue::ackMessage(TWindowData *dat, WPARAM wParam, LPARAM lParam)
{
	ACKDATA *ack = (ACKDATA *)lParam;

	TContainerData *m_pContainer = 0;
	if (dat)
		m_pContainer = dat->pContainer;

	int iFound = (int)(LOWORD(wParam));
	SendJob &job = m_jobs[iFound];

	if (job.iStatus == SQ_ERROR) { // received ack for a job which is already in error state...
		if (dat) {                  // window still open
			if (dat->iCurrentQueueError == iFound) {
				dat->iCurrentQueueError = -1;
				showErrorControls(dat, FALSE);
			}
		}
		// we must discard this job, because there is no message window open to handle the
		// error properly. But we display a tray notification to inform the user about the problem.
		else goto inform_and_discard;
	}

	// failed acks are only handled when the window is still open. with no window open, they will be *silently* discarded

	if (ack->result == ACKRESULT_FAILED) {
		if (dat) {
			// "hard" errors are handled differently in multisend. There is no option to retry - once failed, they
			// are discarded and the user is notified with a small log message.
			if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND))
				SkinPlaySound("SendError");

			TCHAR *szAckMsg = mir_a2t((char *)ack->lParam);
			mir_sntprintf(job.szErrorMsg, TranslateT("Delivery failure: %s"), szAckMsg);
			job.iStatus = SQ_ERROR;
			mir_free(szAckMsg);
			KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound);
			if (!(dat->dwFlags & MWF_ERRORSTATE))
				handleError(dat, iFound);
			return 0;
		}

	inform_and_discard:
		_DebugPopup(job.hContact, TranslateT("A message delivery has failed after the contacts chat window was closed. You may want to resend the last message"));
		clearJob(iFound);
		return 0;
	}

	DBEVENTINFO dbei = { sizeof(dbei) };
	dbei.eventType = EVENTTYPE_MESSAGE;
	dbei.flags = DBEF_SENT;
	dbei.szModule = GetContactProto(job.hContact);
	dbei.timestamp = time(NULL);
	dbei.cbBlob = (int)mir_strlen(job.szSendBuffer) + 1;

	if (dat)
		dat->cache->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1);
	else {
		CContactCache *cc = CContactCache::getContactCache(job.hContact);
		if (cc)
			cc->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1);
	}

	if (job.dwFlags & PREF_RTL)
		dbei.flags |= DBEF_RTL;
	dbei.flags |= DBEF_UTF;
	dbei.pBlob = (PBYTE)job.szSendBuffer;

	MessageWindowEvent evt = { sizeof(evt), (INT_PTR)job.hSendId, job.hContact, &dbei };
	NotifyEventHooks(PluginConfig.m_event_WriteEvent, 0, (LPARAM)&evt);

	job.szSendBuffer = (char*)dbei.pBlob;
	MEVENT hNewEvent = db_event_add(job.hContact, &dbei);

	if (m_pContainer)
		if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND))
			SkinPlaySound("SendMsg");

	M.BroadcastMessage(DM_APPENDMCEVENT, job.hContact, LPARAM(hNewEvent));

	job.hSendId = NULL;
	job.iAcksNeeded--;

	if (job.iAcksNeeded == 0) {              // everything sent
		clearJob(iFound);
		if (dat) {
			KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound);
			dat->iOpenJobs--;
		}
		m_currentIndex--;
	}
	if (dat) {
		checkQueue(dat);

		int iNextFailed = findNextFailed(dat);
		if (iNextFailed >= 0 && !(dat->dwFlags & MWF_ERRORSTATE))
			handleError(dat, iNextFailed);
		else {
			if (M.GetByte("AutoClose", 0)) {
				if (M.GetByte("adv_AutoClose_2", 0))
					SendMessage(dat->hwnd, WM_CLOSE, 0, 1);
				else
					SendMessage(dat->pContainer->hwnd, WM_CLOSE, 0, 0);
			}
		}
	}
	return 0;
}
コード例 #16
0
ファイル: NodeVersionLoader.cpp プロジェクト: yvt/Merlion
	void NodeVersionLoader::receiveChunk()
	{
		auto self = shared_from_this();
		checkThread();
		
		auto& item = currentDownloadItem();
		auto chunkSize = static_cast<std::size_t>
		(std::min<std::uint64_t>(readBuffer.size(), static_cast<std::uint64_t>(remainingBytes)));
		
		if (chunkSize == 0) {
			// Done!
			BOOST_LOG_SEV(log, LogLevel::Info) <<
			"Download completed.";
			
			auto ver = item.version;
			{
				std::lock_guard<std::mutex> lock(downloadQueueMutex);
				downloadQueue.pop_front();
				// note: `item` is a dangling reference now
			}
			
			file.reset();
			timer.cancel();
			
			onVersionDownloaded(ver);
			
			log.setChannel(std::string("(none)"));
			
			state = State::Waiting;
			timer.expires_from_now(boost::posix_time::seconds(1));
			timer.async_wait([this, self] (const error_code& error) {
				if (error) {
					return;
				}
				if (disposed) {
					BOOST_LOG_SEV(log, LogLevel::Debug) <<
					"Retry timer aborted.: Disposed.";
					return;
				}
				state = State::Idle;
				checkQueue();
			});
			return;
		}
		
		
		asio::async_read(socket, asio::buffer(readBuffer.data(), chunkSize), [this, self, &item, chunkSize](const error_code& error, std::size_t) {
			checkThread();
			
			if (disposed) {
				downloadCancelled();
				return;
			}
			if (error) {
				downloadFailed(error.message());
				return;
			}
			
			lastReceiveTime = boost::posix_time::second_clock::local_time();
			
			try {
				file->write(readBuffer.data(), chunkSize);
			} catch (...) {
				downloadFailed("Failed to write to the downloaded file.: " +
							   boost::current_exception_diagnostic_information());
				return;
			}
			remainingBytes -= static_cast<std::uint64_t>(chunkSize);
			
			receiveChunk();
		});
		
		
	}