// File: /workspace/github_c_makefile_star2_O0/sharpyfox[P]citrusleaf-client/src/cl_async.c
// Function: async_receiver_fn
static void* 
async_receiver_fn(void *thdata)
{
	int 		rv = -1;
	bool 		network_error = false;
	cl_async_work	*workitem = NULL;
	cl_async_work	*tmpworkitem = NULL;
	as_msg 		msg;
	cf_queue	*q_to_use = NULL;
	cl_cluster_node	*thisnode = NULL;

	uint8_t		rd_stack_buf[STACK_BUF_SZ];	
	uint8_t		*rd_buf = rd_stack_buf;
	size_t		rd_buf_sz = 0;

	uint64_t	acktrid;
	uint64_t	starttime, endtime;
	int		progress_timeout_ms;
	unsigned int 	thread_id = cf_atomic32_incr(&g_thread_count);

	if (thdata == NULL) {
		q_to_use = g_cl_async_q;
	} else {
		thisnode = (cl_cluster_node *)thdata;
		q_to_use = thisnode->asyncwork_q;
	}
    
	//Infinite loop which keeps picking work items from the list and try to find the end result 
	while(1) {
		network_error = false;
#if ONEASYNCFD
		if(thisnode->dunned == true) {
			do {
				rv = cf_queue_pop(thisnode->asyncwork_q, &workitem, CF_QUEUE_NOWAIT);
				if (rv == CF_QUEUE_OK) {
					cl_cluster_node_put(thisnode);
					free(workitem);
				}
			} while (rv == CF_QUEUE_OK);

			//We want to delete all the workitems of this node
			shash_reduce_delete(g_cl_async_hashtab, cl_del_node_asyncworkitems, thisnode);
			break;
		}
#endif
		//This call will block if there is no element in the queue
		cf_queue_pop(q_to_use, &workitem, CF_QUEUE_FOREVER);

		// Check for thread shutdown message.
		if (workitem->fd == -1) {
			// Exit thread. Workitem will be freed in citrusleaf_async_shutdown().
			pthread_exit(NULL);
		}

		//TODO: What if the node gets dunned while this pop call is blocked ?
#if ONEASYNCFD
		//cf_debug("Elements remaining in this node's queue=%d, Hash table size=%d",
		//		cf_queue_sz(thisnode->asyncwork_q), shash_get_size(g_cl_async_hashtab));
#endif

		// If we have no progress in 50ms, we should move to the next workitem 
		// and revisit this workitem at a later stage
		progress_timeout_ms = DEFAULT_PROGRESS_TIMEOUT;

		// Read into this fine cl_msg, which is the short header
		rv = cf_socket_read_timeout(workitem->fd, (uint8_t *) &msg, sizeof(as_msg), workitem->deadline, progress_timeout_ms);
		if (rv) {
#if DEBUG
			cf_debug("Citrusleaf: error when reading header from server - rv %d fd %d", rv, workitem->fd);
#endif
			if (rv != ETIMEDOUT) {
				cf_error("Citrusleaf: error when reading header from server - rv %d fd %d",rv,workitem->fd);
				network_error = true;
				goto Error;
			} else {
				goto Retry;
			}

		}
#ifdef DEBUG_VERBOSE
		dump_buf("read header from cluster", (uint8_t *) &msg, sizeof(cl_msg));
#endif
		cl_proto_swap(&msg.proto);
		cl_msg_swap_header(&msg.m);

		// second read for the remainder of the message 
		rd_buf_sz =  msg.proto.sz  - msg.m.header_sz;
		if (rd_buf_sz > 0) {
			if (rd_buf_sz > sizeof(rd_stack_buf)) {
				rd_buf = malloc(rd_buf_sz);
				if (!rd_buf) {
					cf_error("malloc fail: trying %zu",rd_buf_sz);
					rv = -1; 
					goto Error; 
				}
			}

			rv = cf_socket_read_timeout(workitem->fd, rd_buf, rd_buf_sz, workitem->deadline, progress_timeout_ms);
			if (rv) {
				//We already read some part of the message before but failed to read the
				//remaining data for whatever reason (network error or timeout). We cannot
				//reread as we already read partial data. Declare this as error.
				cf_error("Timeout after reading the header but before reading the body");
				goto Error;
			}
#ifdef DEBUG_VERBOSE
			dump_buf("read body from cluster", rd_buf, rd_buf_sz);
#endif	
		}

		rv = CITRUSLEAF_OK;
		goto Ok;

Retry:
		//We are trying to postpone the reading
		if (workitem->deadline && workitem->deadline < cf_getms()) {
			cf_error("async receiver: out of time : node %s: deadline %"PRIu64" now %"PRIu64,
					workitem->node->name, workitem->deadline, cf_getms());
			//cf_error("async receiver: Workitem missed the final deadline");
			rv = CITRUSLEAF_FAIL_TIMEOUT;
			goto Error;
		} else {
			//We have time. Push the element back to the queue to be considered later
			cf_queue_push(q_to_use, &workitem);
		}

		//If we allocated memory in this loop, release it.
		if (rd_buf && (rd_buf != rd_stack_buf)) {
			free(rd_buf);
		}

		cf_atomic_int_incr(&g_async_stats.retries);

		continue;

Error:
		if (network_error == true) {
			/* 
			 * In case of Async work (for XDS), it may be extreme to
			 * dun a node in case of network error. We just cleanup
			 * things and retry to connect to the remote cluster.
			 * The network error may be a transient one.
			 */
		} 

#if ONEASYNCFD
//Do not close FD
#else
		//We do not know the state of FD. It may have pending data to be read.
		//We cannot reuse the FD. So, close it to be on safe side.
		cf_error("async receiver: Closing the fd %d because of error", workitem->fd);
		close(workitem->fd);
		workitem->fd = -1;
#endif
		cf_atomic_int_incr(&g_async_stats.dropouts);
		//Continue down with what we do during an Ok

		//Inform the caller that there is no response from the server for this workitem.
		//No response does not mean that the work is not done. The work might be 
		//successfully completed on the server side, we just didnt get response for it.
		if (g_fail_cb_fn) {
			g_fail_cb_fn(workitem->udata, rv, workitem->starttime);
		}
Ok:
		//rd_buf may not be there during an error condition.
		if (rd_buf && (rv == CITRUSLEAF_OK)) {
			//As of now, async functionality is there only for put call.
			//In put call, we do not get anything back other than the trid field.
			//So, just pass variable to get back the trid and ignore others.
			if (0 != cl_parse(&msg.m, rd_buf, rd_buf_sz, NULL, NULL, NULL, &acktrid, NULL)) {
				rv = CITRUSLEAF_FAIL_UNKNOWN;
			}
			else {
				rv = msg.m.result_code;
				if (workitem->trid != acktrid) {
#if ONEASYNCFD
					//It is likely that we may get response for a different trid.
					//Just delete the correct one from the queue 
					//put back the current workitem back in the queue.
					shash_get(g_cl_async_hashtab, &acktrid, &tmpworkitem);
					cf_queue_delete(q_to_use, &tmpworkitem, true);
					cf_queue_push(q_to_use, &workitem);
					//From now on workitem will be the one for which we got ack
					workitem = tmpworkitem;
#endif
#ifdef DEBUG
					cf_debug("Got reply for a different trid. Expected=%"PRIu64" Got=%"PRIu64" FD=%d",
							workitem->trid, acktrid, workitem->fd);
#endif
				}
			}

			if (g_success_cb_fn) {
				g_success_cb_fn(workitem->udata, rv, workitem->starttime);
			}
		}

		//Remember to put back the FD into the pool, if it is re-usable.
		if (workitem->fd != -1) {
			cl_cluster_node_fd_put(workitem->node, workitem->fd, true);
		}
		//Also decrement the reference count for this node
		cl_cluster_node_put(workitem->node);

#if ONEASYNCFD
		//Delete the item from the global hashtable
		if (shash_delete(g_cl_async_hashtab, &workitem->trid) != SHASH_OK)
		{
#if DEBUG
			cf_debug("Failure while trying to delete trid=%"PRIu64" from hashtable", workitem->trid);
#endif
		}
#endif

		//Push it back into the free pool. If the attempt fails, free it.
		if (cf_queue_push(g_cl_workitems_freepool_q, &workitem) == -1) {
			free(workitem);
		}

		//If we allocated memory in this loop, release it.
		if (rd_buf && (rd_buf != rd_stack_buf)) {
			free(rd_buf);
		}

		// Kick this thread out if its ID is greater than total
		if (thread_id > cf_atomic32_get(g_async_num_threads)) {
			cf_atomic32_decr(&g_thread_count);
			return NULL;
		}
	}//The infnite loop

	return NULL;
}

// Assembly:
subq $0x40b8, %rsp           # imm = 0x40B8
movq %rdi, 0x40b0(%rsp)
movl $0xffffffff, 0x40ac(%rsp) # imm = 0xFFFFFFFF
movb $0x0, 0x40ab(%rsp)
movq $0x0, 0x40a0(%rsp)
movq $0x0, 0x4098(%rsp)
movq $0x0, 0x4070(%rsp)
movq $0x0, 0x4068(%rsp)
leaq 0x60(%rsp), %rax
movq %rax, 0x58(%rsp)
movq $0x0, 0x50(%rsp)
leaq 0xfd5c(%rip), %rdi      # 0x205d8
movl $0x1, %esi
callq 0x10f90
movl %eax, 0x30(%rsp)
cmpq $0x0, 0x40b0(%rsp)
jne 0x108a9
leaq 0xfbf4(%rip), %rax      # 0x20490
movq (%rax), %rax
movq %rax, 0x4070(%rsp)
jmp 0x108d0
movq 0x40b0(%rsp), %rax
movq %rax, 0x4068(%rsp)
movq 0x4068(%rsp), %rax
movq 0x88(%rax), %rax
movq %rax, 0x4070(%rsp)
jmp 0x108d2
movb $0x0, 0x40ab(%rsp)
movq 0x4070(%rsp), %rdi
leaq 0x40a0(%rsp), %rsi
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
callq 0x14590
movq 0x40a0(%rsp), %rax
cmpl $-0x1, 0x20(%rax)
jne 0x1090b
xorl %eax, %eax
movl %eax, %edi
callq 0x24e0
movl $0x32, 0x34(%rsp)
movq 0x40a0(%rsp), %rax
movl 0x20(%rax), %edi
movq 0x40a0(%rsp), %rax
movq 0x8(%rax), %rcx
movl 0x34(%rsp), %r8d
leaq 0x407a(%rsp), %rsi
movl $0x1e, %edx
callq 0x15350
movl %eax, 0x40ac(%rsp)
cmpl $0x0, 0x40ac(%rsp)
je 0x109a7
cmpl $0x6e, 0x40ac(%rsp)
je 0x109a2
leaq 0xd975(%rip), %rax      # 0x1e2d8
movl (%rax), %ecx
xorl %eax, %eax
cmpl %ecx, %eax
jg 0x10995
leaq 0xd96e(%rip), %rax      # 0x1e2e0
movq (%rax), %r8
movl 0x40ac(%rsp), %edx
movq 0x40a0(%rsp), %rax
movl 0x20(%rax), %ecx
xorl %edi, %edi
leaq 0x8945(%rip), %rsi      # 0x192d5
movb $0x0, %al
callq *%r8
movb $0x1, 0x40ab(%rsp)
jmp 0x10bbf
jmp 0x10acb
leaq 0x407a(%rsp), %rdi
callq 0x13a70
leaq 0x4082(%rsp), %rdi
callq 0x13af0
movl 0x407c(%rsp), %eax
movzwl 0x4080(%rsp), %ecx
shlq $0x20, %rcx
orq %rcx, %rax
movzbl 0x4082(%rsp), %ecx
subq %rcx, %rax
movq %rax, 0x50(%rsp)
cmpq $0x0, 0x50(%rsp)
jbe 0x10abb
cmpq $0x4000, 0x50(%rsp)     # imm = 0x4000
jbe 0x10a52
movq 0x50(%rsp), %rdi
callq 0x2310
movq %rax, 0x58(%rsp)
cmpq $0x0, 0x58(%rsp)
jne 0x10a50
leaq 0xd8bc(%rip), %rax      # 0x1e2d8
movl (%rax), %ecx
xorl %eax, %eax
cmpl %ecx, %eax
jg 0x10a40
leaq 0xd8b5(%rip), %rax      # 0x1e2e0
movq (%rax), %rcx
movq 0x50(%rsp), %rdx
xorl %edi, %edi
leaq 0x7f36(%rip), %rsi      # 0x18972
movb $0x0, %al
callq *%rcx
movl $0xffffffff, 0x40ac(%rsp) # imm = 0xFFFFFFFF
jmp 0x10bbf
jmp 0x10a52
movq 0x40a0(%rsp), %rax
movl 0x20(%rax), %edi
movq 0x58(%rsp), %rsi
movq 0x50(%rsp), %rdx
movq 0x40a0(%rsp), %rax
movq 0x8(%rax), %rcx
movl 0x34(%rsp), %r8d
callq 0x15350
movl %eax, 0x40ac(%rsp)
cmpl $0x0, 0x40ac(%rsp)
je 0x10ab9
leaq 0xd843(%rip), %rax      # 0x1e2d8
movl (%rax), %ecx
xorl %eax, %eax
cmpl %ecx, %eax
jg 0x10ab4
leaq 0xd83c(%rip), %rax      # 0x1e2e0
movq (%rax), %rcx
xorl %edi, %edi
leaq 0x8865(%rip), %rsi      # 0x19315
movb $0x0, %al
callq *%rcx
jmp 0x10bbf
jmp 0x10abb
movl $0x0, 0x40ac(%rsp)
jmp 0x10c6b
movq 0x40a0(%rsp), %rax
cmpq $0x0, 0x8(%rax)
je 0x10b73
movq 0x40a0(%rsp), %rax
movq 0x8(%rax), %rax
movq %rax, 0x28(%rsp)
callq 0x110d0
movq %rax, %rcx
movq 0x28(%rsp), %rax
cmpq %rcx, %rax
jae 0x10b73
leaq 0xd7d0(%rip), %rax      # 0x1e2d8
movl (%rax), %ecx
xorl %eax, %eax
cmpl %ecx, %eax
jg 0x10b66
leaq 0xd7c9(%rip), %rax      # 0x1e2e0
movq (%rax), %rax
movq %rax, 0x20(%rsp)
movq 0x40a0(%rsp), %rax
movq 0x18(%rax), %rax
movq %rax, 0x10(%rsp)
movq 0x40a0(%rsp), %rax
movq 0x8(%rax), %rax
movq %rax, 0x18(%rsp)
callq 0x110d0
movq 0x10(%rsp), %rdx
movq 0x18(%rsp), %rcx
movq 0x20(%rsp), %r9
movq %rax, %r8
xorl %edi, %edi
leaq 0x87f1(%rip), %rsi      # 0x19352
movb $0x0, %al
callq *%r9
movl $0xfffffffe, 0x40ac(%rsp) # imm = 0xFFFFFFFE
jmp 0x10bbf
movq 0x4070(%rsp), %rdi
leaq 0x40a0(%rsp), %rsi
callq 0x14300
cmpq $0x0, 0x58(%rsp)
je 0x10ba9
movq 0x58(%rsp), %rax
leaq 0x60(%rsp), %rcx
cmpq %rcx, %rax
je 0x10ba9
movq 0x58(%rsp), %rdi
callq 0x22e0
leaq 0xf8e8(%rip), %rdi      # 0x20498
movl $0x1, %esi
callq 0x11150
jmp 0x108d2
movb 0x40ab(%rsp), %al
andb $0x1, %al
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x10bd2
jmp 0x10bd2
leaq 0xd6ff(%rip), %rax      # 0x1e2d8
movl (%rax), %ecx
xorl %eax, %eax
cmpl %ecx, %eax
jg 0x10c03
leaq 0xd6f8(%rip), %rax      # 0x1e2e0
movq (%rax), %rcx
movq 0x40a0(%rsp), %rax
movl 0x20(%rax), %edx
xorl %edi, %edi
leaq 0x878f(%rip), %rsi      # 0x1938e
movb $0x0, %al
callq *%rcx
movq 0x40a0(%rsp), %rax
movl 0x20(%rax), %edi
callq 0x2570
movq 0x40a0(%rsp), %rax
movl $0xffffffff, 0x20(%rax) # imm = 0xFFFFFFFF
leaq 0xf86f(%rip), %rdi      # 0x20498
addq $0x8, %rdi
movl $0x1, %esi
callq 0x11150
cmpq $0x0, 0xf981(%rip)      # 0x205c0
je 0x10c69
movq 0xf978(%rip), %rax      # 0x205c0
movq 0x40a0(%rsp), %rcx
movq 0x28(%rcx), %rdi
movl 0x40ac(%rsp), %esi
movq 0x40a0(%rsp), %rcx
movq 0x10(%rcx), %rdx
callq *%rax
jmp 0x10c6b
cmpq $0x0, 0x58(%rsp)
je 0x10d2b
cmpl $0x0, 0x40ac(%rsp)
jne 0x10d2b
leaq 0x407a(%rsp), %rdi
addq $0x8, %rdi
movq 0x58(%rsp), %rsi
movq 0x50(%rsp), %rdx
xorl %eax, %eax
movl %eax, %r9d
leaq 0x48(%rsp), %rax
movq %r9, %rcx
movq %r9, %r8
movq %rax, (%rsp)
movq $0x0, 0x8(%rsp)
callq 0x6dc0
movl %eax, %ecx
xorl %eax, %eax
cmpl %ecx, %eax
je 0x10cd2
movl $0x1, 0x40ac(%rsp)
jmp 0x10cf7
movzbl 0x4087(%rsp), %eax
movl %eax, 0x40ac(%rsp)
movq 0x40a0(%rsp), %rax
movq (%rax), %rax
cmpq 0x48(%rsp), %rax
je 0x10cf5
jmp 0x10cf5
jmp 0x10cf7
cmpq $0x0, 0xf8c9(%rip)      # 0x205c8
je 0x10d29
movq 0xf8c0(%rip), %rax      # 0x205c8
movq 0x40a0(%rsp), %rcx
movq 0x28(%rcx), %rdi
movl 0x40ac(%rsp), %esi
movq 0x40a0(%rsp), %rcx
movq 0x10(%rcx), %rdx
callq *%rax
jmp 0x10d2b
movq 0x40a0(%rsp), %rax
cmpl $-0x1, 0x20(%rax)
je 0x10d5a
movq 0x40a0(%rsp), %rax
movq 0x18(%rax), %rdi
movq 0x40a0(%rsp), %rax
movl 0x20(%rax), %esi
movl $0x1, %edx
callq 0xc230
movq 0x40a0(%rsp), %rax
movq 0x18(%rax), %rdi
callq 0xbe40
leaq 0xf73e(%rip), %rax      # 0x204b0
movq (%rax), %rdi
leaq 0x40a0(%rsp), %rsi
callq 0x14300
cmpl $-0x1, %eax
jne 0x10d94
movq 0x40a0(%rsp), %rdi
callq 0x22e0
cmpq $0x0, 0x58(%rsp)
je 0x10db5
movq 0x58(%rsp), %rax
leaq 0x60(%rsp), %rcx
cmpq %rcx, %rax
je 0x10db5
movq 0x58(%rsp), %rdi
callq 0x22e0
movl 0x30(%rsp), %eax
movl 0xf6fd(%rip), %ecx      # 0x204bc
cmpl %ecx, %eax
jbe 0x10dde
leaq 0xf80e(%rip), %rdi      # 0x205d8
movl $0xffffffff, %esi       # imm = 0xFFFFFFFF
callq 0x10f90
xorl %eax, %eax
addq $0x40b8, %rsp           # imm = 0x40B8
retq
jmp 0x108d2
nopw %cs:(%rax,%rax)
nopl (%rax)