// File: /workspace/github_c_makefile_star2_O3/sharpyfox[P]citrusleaf-client/src/cl_async.c
// Function: cl_do_async_monte
int
cl_do_async_monte(cl_cluster *asc, int info1, int info2, const char *ns, const char *set, const cl_object *key,
			const cf_digest *digest, cl_bin **values, cl_operator operator, cl_operation **operations, 
			int *n_values, uint32_t *cl_gen, const cl_write_parameters *cl_w_p, uint64_t *trid, void *udata)

{
	cl_async_work	*workitem = NULL;

	uint8_t		wr_stack_buf[STACK_BUF_SZ];
	uint8_t		*wr_buf = wr_stack_buf;
	size_t		wr_buf_sz = sizeof(wr_stack_buf);
	int        	progress_timeout_ms;
	uint64_t 	deadline_ms;
	uint64_t	starttime, endtime;
	bool 		network_error;
	int 		fd = -1;
	int		rv = CITRUSLEAF_FAIL_CLIENT;	//Assume that this is a failure;

	as_msg 		msg;
	cf_digest	d_ret;
	cl_cluster_node	*node = 0;

#if ONEASYNCFD
	if (shash_get_size(g_cl_async_hashtab) >= g_async_h_szlimit) {
		//cf_error("Async hashtab is full. Cannot insert any more elements");
		return CITRUSLEAF_FAIL_ASYNCQ_FULL;
	}
#else
	//If the async buffer is at the max limit, do not entertain more requests.
	if (cf_queue_sz(g_cl_async_q) >= cf_atomic32_get(g_async_q_szlimit)) {
		//cf_error("Async buffer is full. Cannot insert any more elements");
		return CITRUSLEAF_FAIL_ASYNCQ_FULL;
	}
#endif

	//Allocate memory for work item that will be added to the async work list

	if (cf_queue_sz(g_cl_workitems_freepool_q) > 0) {
		cf_queue_pop(g_cl_workitems_freepool_q, &workitem, CF_QUEUE_FOREVER);
	} else {
		workitem = malloc(sizeof(cl_async_work));
		if (workitem == NULL) {
			return CITRUSLEAF_FAIL_CLIENT;
		}
	}

	//Compile the write buffer to be sent to the cluster
	if (n_values && ( values || operations) ){
		cl_compile(info1, info2, 0, ns, set, key, digest, values?*values:NULL, operator, operations?*operations:NULL,
				*n_values , &wr_buf, &wr_buf_sz, cl_w_p, &d_ret, *trid,NULL);
	}else{
		cl_compile(info1, info2, 0, ns, set, key, digest, 0, 0, 0, 0, &wr_buf, &wr_buf_sz, cl_w_p, &d_ret, *trid,NULL);
	}	

	deadline_ms = 0;
	progress_timeout_ms = 0;
	if (cl_w_p && cl_w_p->timeout_ms) {
		deadline_ms = cf_getms() + cl_w_p->timeout_ms;
		// policy: if asking for a long timeout, give enough time to try twice
		if (cl_w_p->timeout_ms > 700) {
			progress_timeout_ms = cl_w_p->timeout_ms / 2;
		}
		else {
			progress_timeout_ms = cl_w_p->timeout_ms;
		}
	}
	else {
		progress_timeout_ms = g_async_nw_progress_timeout;
	}

	//Initialize the async work unit
	workitem->trid = *trid;
	workitem->deadline = deadline_ms;
	workitem->starttime = cf_getms();
	workitem->udata = udata;

	int try = 0;
	// retry request based on the write_policy
	do {
		network_error = false;
		try++;
#ifdef DEBUG		
		if (try > 1) {
			cf_debug("request retrying try %d tid %zu", try, (uint64_t)pthread_self());
		}
#endif        

		// Get an FD from a cluster. First get the probable node for the given digest.
		node = cl_cluster_node_get(asc, ns, &d_ret, info2 & CL_MSG_INFO2_WRITE ? true : false);
		if (!node) {
#ifdef DEBUG
			cf_debug("warning: no healthy nodes in cluster, retrying");
#endif
			usleep(10000);	//Sleep for 10ms
			goto Retry;
		}

		// Now get the dedicated async FD of this node
		starttime = cf_getms();
		fd = cl_cluster_node_fd_get(node, true, asc->nbconnect);
		endtime = cf_getms();
		if ((endtime - starttime) > 10) {
			cf_debug("Time to get FD for a node (>10ms)=%"PRIu64, (endtime - starttime));
		}
		if (fd == -1) {
#ifdef DEBUG			
			cf_debug("warning: node %s has no async file descriptors, retrying transaction (tid %zu)",node->name,(uint64_t)pthread_self() );
#endif			
			usleep(1000);
			goto Retry;
		}

		// Hate special cases, but we have to clear the verify bit on delete verify
		if ( (info2 & CL_MSG_INFO2_DELETE) && (info1 & CL_MSG_INFO1_VERIFY))
		{
			as_msg *msgp = (as_msg *)wr_buf;
			msgp->m.info1 &= ~CL_MSG_INFO1_VERIFY;
		}
		
		// Send the command to the node
		starttime = cf_getms();
		rv = cf_socket_write_timeout(fd, wr_buf, wr_buf_sz, deadline_ms, progress_timeout_ms);
		endtime = cf_getms();
		if ((endtime - starttime) > 10) {
			cf_debug("Time to write to the socket (>10ms)=%"PRIu64, (endtime - starttime));
		}
		if (rv != 0) {
			cf_debug("Citrusleaf: write timeout or error when writing header to server - %d fd %d errno %d (tid %zu)",
					rv,fd,errno,(uint64_t)pthread_self());
			if (rv != ETIMEDOUT)
				network_error = true;
			goto Retry;
		}
		goto Ok;

Retry:
		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. As this is a
			 * network error, its is better to wait for some significant
			 * time before retrying.
			 */
			sleep(1);	//Sleep for 1sec
#if ONEASYNCFD
//Do not close the FD
#else
			cf_error("async sender: Closing the fd %d because of network error", fd);
			close(fd);
			fd = -1;
#endif
		}

		if (fd != -1) {
			cf_error("async sender: Closing the fd %d because of retry", fd);
			close(fd);
			fd = -1;
		}

		if (node) {
			cl_cluster_node_put(node); 
			node = 0; 
		}

		if (deadline_ms && (deadline_ms < cf_getms() ) ) {
#ifdef DEBUG            
			cf_debug("async sender: out of time : deadline %"PRIu64" now %"PRIu64, deadline_ms, cf_getms());
#endif            
			rv = CITRUSLEAF_FAIL_TIMEOUT;
			goto Error;
		}
	} while ( (cl_w_p == 0) || (cl_w_p->w_pol == CL_WRITE_RETRY) );

Error:	
#ifdef DEBUG	
	cf_debug("exiting with failure: network_error %d wpol %d timeleft %d rv %d",
			(int)network_error, (int)(cl_w_p ? cl_w_p->w_pol : 0), 
			(int)(deadline_ms - cf_getms() ), rv );
#endif	

	if (wr_buf != wr_stack_buf) {
		free(wr_buf);
	}

#if ONEASYNCFD
	//Do not close the FD
#else
	//If it is a network error, the fd would be closed and set to -1.
	//So, we reach this place with a valid FD in case of timeout.
	if (fd != -1) {
		cf_error("async sender: Closing the fd %d because of timeout", fd);
		close(fd);
	}
#endif

	return(rv);
Ok:
	/*
	 * We cannot release the node here as the asyc FD associated
	 * with this node may get closed. We should do it only when
	 * we got back the ack for the async command that we just did.
	 */

	//As we sent the command successfully, add it to the async work list
	workitem->node = node;
	workitem->fd = fd;
	//We are storing only the pointer to the workitem
#if ONEASYNCFD
	if (shash_put_unique(g_cl_async_hashtab, trid, &workitem) != SHASH_OK) {
		//This should always succeed.
		cf_error("Unable to add unique entry into the hash table");
	}
	cf_queue_push(node->asyncwork_q, &workitem);	//Also put in the node's q
#else
	cf_queue_push(g_cl_async_q, &workitem);
#endif

	if (wr_buf != wr_stack_buf) {
		free(wr_buf);
	}

	rv = CITRUSLEAF_OK;
	return rv;

}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x4078, %rsp           # imm = 0x4078
leaq 0x70(%rsp), %rax
movq %rdi, 0x50(%rsp)
movq %r9, %r14
movq %r8, %r15
movq %rcx, 0x30(%rsp)
movl %edx, %r12d
movl %esi, %ebp
movq $0x0, 0x20(%rsp)
movq $0x4000, 0x38(%rsp)     # imm = 0x4000
movq %rax, 0x18(%rsp)
leaq 0xe23f(%rip), %rax      # 0x18480
movq (%rax), %rdi
callq 0xca36
cmpl 0xe249(%rip), %eax      # 0x18498
movl $0xfffffffd, %ebx       # imm = 0xFFFFFFFD
jge 0xa7a8
leaq 0xe23f(%rip), %rbx      # 0x184a0
movq (%rbx), %rdi
callq 0xca36
testl %eax, %eax
jle 0xa281
movq (%rbx), %rdi
leaq 0x20(%rsp), %rsi
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
callq 0xccdc
jmp 0xa299
movl $0x30, %edi
callq 0x22e0
testq %rax, %rax
movq %rax, 0x20(%rsp)
je 0xa320
movq 0x40d0(%rsp), %rcx
movl %ebp, (%rsp)
movq 0x40e0(%rsp), %rbp
movq 0x40b0(%rsp), %rax
testq %rcx, %rcx
je 0xa2db
movq 0x40b8(%rsp), %rsi
movq 0x40c8(%rsp), %rdx
movq %rsi, %rdi
orq %rdx, %rdi
je 0xa2db
testq %rsi, %rsi
je 0xa32a
movq (%rsi), %r10
jmp 0xa32d
subq $0x8, %rsp
movl 0x8(%rsp), %edi
movq 0x38(%rsp), %rcx
xorl %r10d, %r10d
movl %r12d, %esi
xorl %edx, %edx
movq %r15, %r8
movq %r14, %r9
leaq 0x64(%rsp), %r11
leaq 0x40(%rsp), %rbx
leaq 0x20(%rsp), %r13
pushq %r10
movq 0x40f8(%rsp), %r14
pushq (%r14)
pushq %r11
pushq %rbp
pushq %rbx
pushq %r13
pushq %r10
pushq %r10
pushq %r10
jmp 0xa384
movl $0xffffffff, %ebx       # imm = 0xFFFFFFFF
jmp 0xa7a8
xorl %r10d, %r10d
testq %rdx, %rdx
je 0xa337
movq (%rdx), %rbx
jmp 0xa339
xorl %ebx, %ebx
movl (%rcx), %r13d
subq $0x8, %rsp
movl 0x8(%rsp), %edi
movq 0x38(%rsp), %rcx
movl %r12d, %esi
xorl %edx, %edx
movq %r15, %r8
movq %r14, %r9
leaq 0x64(%rsp), %r11
pushq $0x0
movq 0x40f8(%rsp), %r14
pushq (%r14)
pushq %r11
pushq %rbp
leaq 0x60(%rsp), %r11
pushq %r11
leaq 0x48(%rsp), %r11
pushq %r11
pushq %r13
pushq %rbx
movl 0x4108(%rsp), %r11d
pushq %r11
pushq %r10
pushq %rax
callq 0x4b3f
addq $0x60, %rsp
movq 0x40f0(%rsp), %r14
testq %rbp, %rbp
movq %rbp, %r13
je 0xa3e9
cmpl $0x0, 0xc(%r13)
je 0xa3e9
leaq 0x8(%rsp), %rbx
movl $0x1, %edi
movq %rbx, %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %rax # imm = 0x431BDE82D7B634DB
imulq $0x3e8, (%rbx), %r15    # imm = 0x3E8
mulq 0x8(%rbx)
movslq 0xc(%r13), %rax
shrq $0x12, %rdx
addq %rdx, %r15
cmpq $0x2bd, %rax            # imm = 0x2BD
setge %cl
addq %rax, %r15
shrl %cl, %eax
jmp 0xa3f2
movl 0xbed5(%rip), %eax      # 0x162c4
xorl %r15d, %r15d
movq %rax, 0x48(%rsp)
movq 0x40e8(%rsp), %rax
movq 0x20(%rsp), %rcx
movl (%rsp), %ebp
leaq 0x8(%rsp), %rbx
movl $0x1, %edi
movq %rbx, %rsi
movq (%rax), %rax
movq %rax, (%rcx)
movq 0x20(%rsp), %rax
movq %r15, 0x8(%rax)
callq 0x2230
movabsq $0x431bde82d7b634db, %rax # imm = 0x431BDE82D7B634DB
movq 0x20(%rsp), %rcx
movl $0xffffffff, (%rsp)     # imm = 0xFFFFFFFF
movq %r15, 0x28(%rsp)
mulq 0x8(%rbx)
imulq $0x3e8, (%rbx), %rax    # imm = 0x3E8
shrq $0x12, %rdx
addq %rdx, %rax
testb $0x2, %r12b
movq %rax, 0x10(%rcx)
movq %r14, 0x28(%rcx)
movq 0x50(%rsp), %r14
sete %dl
testb $0x8, %bpl
sete %cl
andl $0x1, %r12d
orb %dl, %cl
movl %r12d, 0x44(%rsp)
movb %cl, 0x7(%rsp)
movq 0x30(%rsp), %rsi
movl 0x44(%rsp), %ecx
movq %r14, %rdi
leaq 0x5c(%rsp), %rdx
callq 0x7daf
testq %rax, %rax
je 0xa69d
leaq 0x8(%rsp), %rbx
movl $0x1, %edi
movq %rax, %rbp
movq %rbx, %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %r15 # imm = 0x431BDE82D7B634DB
movq 0x8(%rsp), %r13
movq %rbp, %rdi
movl $0x1, %esi
movq %r15, %rax
mulq 0x10(%rsp)
movq %rdx, %r12
movzbl 0x15(%r14), %edx
shrq $0x12, %r12
callq 0x83f3
movl $0x1, %edi
movq %rbx, %rsi
movl %eax, %r14d
callq 0x2230
movq %r15, %rax
movq 0x8(%rsp), %rcx
mulq 0x10(%rsp)
subq %r13, %rcx
movq %rdx, %rax
imulq $0x3e8, %rcx, %rdx      # imm = 0x3E8
shrq $0x12, %rax
subq %r12, %rax
addq %rax, %rdx
cmpq $0xb, %rdx
jb 0xa545
leaq 0xbda4(%rip), %rax      # 0x162c8
movl (%rax), %eax
cmpl $0x3, %eax
jl 0xa545
leaq 0xbd9e(%rip), %rax      # 0x162d0
movl $0x3, %edi
leaq 0x5b99(%rip), %rsi      # 0x100d7
movq (%rax), %rcx
xorl %eax, %eax
callq *%rcx
cmpl $-0x1, %r14d
je 0xa6a9
cmpb $0x0, 0x7(%rsp)
jne 0xa55f
movq 0x18(%rsp), %rax
andb $-0x9, 0x9(%rax)
leaq 0x8(%rsp), %rbx
movl $0x1, %edi
movq %rbx, %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %r15 # imm = 0x431BDE82D7B634DB
movq 0x18(%rsp), %rsi
movq 0x28(%rsp), %rcx
movq 0x8(%rsp), %r13
movl %r14d, %edi
movq 0x48(%rsp), %r8
movq %r15, %rax
mulq 0x10(%rsp)
movq %rdx, %r12
movq 0x38(%rsp), %rdx
shrq $0x12, %r12
callq 0xdae9
movl $0x1, %edi
movq %rbx, %rsi
movl %eax, (%rsp)
callq 0x2230
movq %r15, %rax
movq 0x8(%rsp), %rcx
mulq 0x10(%rsp)
subq %r13, %rcx
movq %rdx, %rax
imulq $0x3e8, %rcx, %rdx      # imm = 0x3E8
shrq $0x12, %rax
subq %r12, %rax
addq %rax, %rdx
cmpq $0xb, %rdx
jb 0xa60d
leaq 0xbcdc(%rip), %rax      # 0x162c8
movl (%rax), %eax
cmpl $0x3, %eax
jl 0xa60d
leaq 0xbcd6(%rip), %rax      # 0x162d0
movl $0x3, %edi
leaq 0x5af7(%rip), %rsi      # 0x100fd
movq (%rax), %rcx
xorl %eax, %eax
callq *%rcx
movl (%rsp), %ebx
testl %ebx, %ebx
je 0xa773
leaq 0xbca9(%rip), %rax      # 0x162c8
movl (%rax), %eax
cmpl $0x3, %eax
jl 0xa659
leaq 0xbca3(%rip), %rax      # 0x162d0
movq (%rax), %r13
callq 0x2040
movl (%rax), %r12d
callq 0x2210
movq %rax, %r9
movl $0x3, %edi
leaq 0x5ad9(%rip), %rsi      # 0x10125
movl %ebx, %edx
movl %r14d, %ecx
movl %r12d, %r8d
xorl %eax, %eax
callq *%r13
cmpl $0x6e, %ebx
jne 0xa6bd
leaq 0xbc63(%rip), %rax      # 0x162c8
movq 0x40e0(%rsp), %r13
cmpl $0x0, (%rax)
js 0xa68c
leaq 0xbc57(%rip), %rax      # 0x162d0
xorl %edi, %edi
leaq 0x5b3b(%rip), %rsi      # 0x101bd
movl %r14d, %edx
movq (%rax), %rcx
xorl %eax, %eax
callq *%rcx
movl %r14d, %edi
callq 0x2550
movl $0x6e, (%rsp)
jmp 0xa6fd
movl $0x2710, %edi           # imm = 0x2710
callq 0x2360
jmp 0xa70f
movl $0x3e8, %edi            # imm = 0x3E8
callq 0x2360
movq 0x40e0(%rsp), %r13
jmp 0xa6fd
movl $0x1, %edi
callq 0x2180
leaq 0xbbfa(%rip), %rax      # 0x162c8
movq 0x40e0(%rsp), %r13
cmpl $0x0, (%rax)
js 0xa6f5
leaq 0xbbee(%rip), %rax      # 0x162d0
xorl %edi, %edi
leaq 0x5a99(%rip), %rsi      # 0x10184
movl %r14d, %edx
movq (%rax), %rcx
xorl %eax, %eax
callq *%rcx
movl %r14d, %edi
callq 0x2550
movq 0x28(%rsp), %r15
movq %rbp, %rdi
callq 0x8227
movq 0x50(%rsp), %r14
testq %r15, %r15
je 0xa747
movl $0x1, %edi
leaq 0x8(%rsp), %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %rax # imm = 0x431BDE82D7B634DB
mulq 0x10(%rsp)
imulq $0x3e8, 0x8(%rsp), %rax # imm = 0x3E8
shrq $0x12, %rdx
addq %rdx, %rax
cmpq %rax, %r15
jb 0xa75d
testq %r13, %r13
je 0xa47f
cmpl $0x2, 0x14(%r13)
je 0xa47f
jmp 0xa764
movl $0xfffffffe, (%rsp)     # imm = 0xFFFFFFFE
movq 0x18(%rsp), %rdi
movl (%rsp), %ebx
leaq 0x70(%rsp), %rax
jmp 0xa79e
leaq 0x20(%rsp), %rsi
movq (%rsi), %rax
movq %rbp, 0x18(%rax)
movl %r14d, 0x20(%rax)
leaq 0xdcf6(%rip), %rax      # 0x18480
movq (%rax), %rdi
callq 0xcb7c
movq 0x18(%rsp), %rdi
leaq 0x70(%rsp), %rax
xorl %ebx, %ebx
cmpq %rax, %rdi
je 0xa7a8
callq 0x22c0
movl %ebx, %eax
addq $0x4078, %rsp           # imm = 0x4078
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq