// File: /workspace/github_c_makefile_star2_O1/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
movq %r9, %r14
movq %r8, %r15
movq %rcx, 0x30(%rsp)
movl %edx, %r12d
movl %esi, %ebp
movq %rdi, 0x50(%rsp)
movq $0x0, 0x20(%rsp)
leaq 0x70(%rsp), %rax
movq %rax, 0x18(%rsp)
movq $0x4000, 0x38(%rsp)     # imm = 0x4000
leaq 0xef2f(%rip), %rax      # 0x19480
movq (%rax), %rdi
callq 0xce4d
movl $0xfffffffd, %ebx       # imm = 0xFFFFFFFD
cmpl 0xef34(%rip), %eax      # 0x19498
jge 0xaa8c
leaq 0xef2f(%rip), %rbx      # 0x194a0
movq (%rbx), %rdi
callq 0xce4d
testl %eax, %eax
jle 0xa591
movq (%rbx), %rdi
leaq 0x20(%rsp), %rsi
movl $0xffffffff, %edx       # imm = 0xFFFFFFFF
callq 0xd0df
jmp 0xa5a5
movl $0x30, %edi
callq 0x22e0
movq %rax, 0x20(%rsp)
testq %rax, %rax
je 0xa61f
movq 0x40e0(%rsp), %r13
movq 0x40d0(%rsp), %rcx
testq %rcx, %rcx
je 0xa5dc
movq 0x40c8(%rsp), %rdx
movq 0x40b8(%rsp), %rsi
movq %rsi, %rdi
orq %rdx, %rdi
je 0xa5dc
testq %rsi, %rsi
je 0xa629
movq (%rsi), %r10
jmp 0xa62c
subq $0x8, %rsp
xorl %r10d, %r10d
leaq 0x64(%rsp), %r11
leaq 0x40(%rsp), %rbx
leaq 0x20(%rsp), %rax
movl %ebp, %edi
movl %r12d, %esi
xorl %edx, %edx
movq 0x38(%rsp), %rcx
movq %r15, %r8
movq %r14, %r9
pushq %r10
movq 0x40f8(%rsp), %r14
pushq (%r14)
pushq %r11
pushq %r13
pushq %rbx
pushq %rax
pushq %r10
pushq %r10
pushq %r10
jmp 0xa67e
movl $0xffffffff, %ebx       # imm = 0xFFFFFFFF
jmp 0xaa8c
xorl %r10d, %r10d
testq %rdx, %rdx
je 0xa636
movq (%rdx), %rbx
jmp 0xa638
xorl %ebx, %ebx
movl (%rcx), %eax
subq $0x8, %rsp
leaq 0x64(%rsp), %r11
movl %ebp, %edi
movl %r12d, %esi
xorl %edx, %edx
movq 0x38(%rsp), %rcx
movq %r15, %r8
movq %r14, %r9
pushq $0x0
movq 0x40f8(%rsp), %r14
pushq (%r14)
pushq %r11
pushq %r13
leaq 0x60(%rsp), %r11
pushq %r11
leaq 0x48(%rsp), %r11
pushq %r11
pushq %rax
pushq %rbx
movl 0x4108(%rsp), %eax
pushq %rax
pushq %r10
pushq 0x4108(%rsp)
callq 0x4b69
addq $0x60, %rsp
movq 0x40f0(%rsp), %r14
testq %r13, %r13
je 0xa6eb
cmpl $0x0, 0xc(%r13)
je 0xa6eb
leaq 0x8(%rsp), %rbx
movl $0x1, %edi
movq %rbx, %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %rax # imm = 0x431BDE82D7B634DB
mulq 0x8(%rbx)
shrq $0x12, %rdx
imulq $0x3e8, (%rbx), %r15    # imm = 0x3E8
addq %rdx, %r15
movslq 0xc(%r13), %rax
cmpq $0x2bd, %rax            # imm = 0x2BD
setge %cl
addq %rax, %r15
shrl %cl, %eax
movq %rax, 0x48(%rsp)
jmp 0xa6f9
movl 0xcbd3(%rip), %eax      # 0x172c4
movq %rax, 0x48(%rsp)
xorl %r15d, %r15d
movq 0x40e8(%rsp), %rax
movq (%rax), %rax
movq 0x20(%rsp), %rcx
movq %rax, (%rcx)
movq 0x20(%rsp), %rax
movq %r15, 0x8(%rax)
leaq 0x8(%rsp), %rbx
movl $0x1, %edi
movq %rbx, %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %rax # imm = 0x431BDE82D7B634DB
mulq 0x8(%rbx)
shrq $0x12, %rdx
imulq $0x3e8, (%rbx), %rax    # imm = 0x3E8
addq %rdx, %rax
movq 0x20(%rsp), %rcx
movq %rax, 0x10(%rcx)
movq %r14, 0x28(%rcx)
testb $0x2, %r12b
sete %al
testb $0x8, %bpl
sete %cl
orb %al, %cl
movb %cl, 0x3(%rsp)
movl $0xffffffff, 0x4(%rsp)  # imm = 0xFFFFFFFF
andl $0x1, %r12d
movl %r12d, 0x44(%rsp)
movq 0x50(%rsp), %r14
movq %r15, 0x28(%rsp)
movq %r14, %rdi
movq 0x30(%rsp), %rsi
leaq 0x5c(%rsp), %rdx
movl 0x44(%rsp), %ecx
callq 0x7f33
movq %rax, %rbp
testq %rax, %rax
je 0xa963
movl $0x1, %edi
leaq 0x8(%rsp), %r15
movq %r15, %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %rbx # imm = 0x431BDE82D7B634DB
movq %rbx, %rax
mulq 0x10(%rsp)
movq %rdx, %r12
shrq $0x12, %r12
movq 0x8(%rsp), %r13
movzbl 0x15(%r14), %edx
movq %rbp, %rdi
movl $0x1, %esi
callq 0x864f
movl %eax, %r14d
movl $0x1, %edi
movq %r15, %rsi
callq 0x2230
movq %rbx, %rax
mulq 0x10(%rsp)
movq %rdx, %rax
shrq $0x12, %rax
subq %r12, %rax
movq 0x8(%rsp), %rcx
subq %r13, %rcx
imulq $0x3e8, %rcx, %rdx      # imm = 0x3E8
addq %rax, %rdx
cmpq $0xb, %rdx
jb 0xa845
leaq 0xcaa4(%rip), %rax      # 0x172c8
movl (%rax), %eax
cmpl $0x3, %eax
jl 0xa845
leaq 0xca9e(%rip), %rax      # 0x172d0
movq (%rax), %rcx
movl $0x3, %edi
leaq 0x6876(%rip), %rsi      # 0x110b7
xorl %eax, %eax
callq *%rcx
cmpl $-0x1, %r14d
je 0xa977
cmpb $0x0, 0x3(%rsp)
jne 0xa85f
movq 0x18(%rsp), %rax
andb $-0x9, 0x9(%rax)
movl $0x1, %edi
leaq 0x8(%rsp), %r15
movq %r15, %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %rbx # imm = 0x431BDE82D7B634DB
movq %rbx, %rax
mulq 0x10(%rsp)
movq %rdx, %r12
shrq $0x12, %r12
movq 0x8(%rsp), %r13
movq 0x18(%rsp), %rsi
movq 0x38(%rsp), %rdx
movl %r14d, %edi
movq 0x28(%rsp), %rcx
movq 0x48(%rsp), %r8
callq 0xdf14
movl %eax, 0x4(%rsp)
movl $0x1, %edi
movq %r15, %rsi
callq 0x2230
movq %rbx, %rax
mulq 0x10(%rsp)
movq %rdx, %rax
shrq $0x12, %rax
subq %r12, %rax
movq 0x8(%rsp), %rcx
subq %r13, %rcx
imulq $0x3e8, %rcx, %rdx      # imm = 0x3E8
addq %rax, %rdx
cmpq $0xb, %rdx
jb 0xa90e
leaq 0xc9db(%rip), %rax      # 0x172c8
movl (%rax), %eax
cmpl $0x3, %eax
jl 0xa90e
leaq 0xc9d5(%rip), %rax      # 0x172d0
movq (%rax), %rcx
movl $0x3, %edi
leaq 0x67d3(%rip), %rsi      # 0x110dd
xorl %eax, %eax
callq *%rcx
movl 0x4(%rsp), %ebx
testl %ebx, %ebx
je 0xaaa0
leaq 0xc9a7(%rip), %rax      # 0x172c8
movl (%rax), %eax
cmpl $0x3, %eax
jl 0xa95b
leaq 0xc9a1(%rip), %rax      # 0x172d0
movq (%rax), %r13
callq 0x2040
movl (%rax), %r12d
callq 0x2210
movl $0x3, %edi
leaq 0x67ba(%rip), %rsi      # 0x11105
movl %ebx, %edx
movl %r14d, %ecx
movl %r12d, %r8d
movq %rax, %r9
xorl %eax, %eax
callq *%r13
cmpl $0x6e, %ebx
setne %al
jmp 0xa989
movl $0x2710, %edi           # imm = 0x2710
callq 0x2360
movl $0xffffffff, %r14d      # imm = 0xFFFFFFFF
xorl %eax, %eax
jmp 0xa996
movl $0x3e8, %edi            # imm = 0x3E8
callq 0x2360
movl $0xffffffff, %r14d      # imm = 0xFFFFFFFF
xorl %eax, %eax
movq 0x40e0(%rsp), %r13
movq 0x28(%rsp), %r15
testb %al, %al
je 0xa9d8
movl $0x1, %edi
callq 0x2180
leaq 0xc91d(%rip), %rax      # 0x172c8
cmpl $0x0, (%rax)
js 0xa9ca
leaq 0xc919(%rip), %rax      # 0x172d0
movq (%rax), %rcx
xorl %edi, %edi
leaq 0x67a1(%rip), %rsi      # 0x11164
movl %r14d, %edx
xorl %eax, %eax
callq *%rcx
movl %r14d, %edi
callq 0x2550
movl $0xffffffff, %r14d      # imm = 0xFFFFFFFF
cmpl $-0x1, %r14d
je 0xaa0c
leaq 0xc8e3(%rip), %rax      # 0x172c8
cmpl $0x0, (%rax)
js 0xaa04
leaq 0xc8df(%rip), %rax      # 0x172d0
movq (%rax), %rcx
xorl %edi, %edi
leaq 0x67a0(%rip), %rsi      # 0x1119d
movl %r14d, %edx
xorl %eax, %eax
callq *%rcx
movl %r14d, %edi
callq 0x2550
testq %rbp, %rbp
je 0xaa19
movq %rbp, %rdi
callq 0x8426
testq %r15, %r15
movq 0x50(%rsp), %r14
je 0xaa56
movl $0x1, %edi
leaq 0x8(%rsp), %rsi
callq 0x2230
movabsq $0x431bde82d7b634db, %rax # imm = 0x431BDE82D7B634DB
mulq 0x10(%rsp)
shrq $0x12, %rdx
imulq $0x3e8, 0x8(%rsp), %rax # imm = 0x3E8
addq %rdx, %rax
cmpq %rax, %r15
jb 0xaa6c
testq %r13, %r13
je 0xa77f
cmpl $0x2, 0x14(%r13)
je 0xa77f
jmp 0xaa74
movl $0xfffffffe, 0x4(%rsp)  # imm = 0xFFFFFFFE
movq 0x18(%rsp), %rdi
leaq 0x70(%rsp), %rax
cmpq %rax, %rdi
movl 0x4(%rsp), %ebx
je 0xaa8c
callq 0x22c0
movl %ebx, %eax
addq $0x4078, %rsp           # imm = 0x4078
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
leaq 0x20(%rsp), %rsi
movq (%rsi), %rax
movq %rbp, 0x18(%rax)
movl %r14d, 0x20(%rax)
leaq 0xe9c9(%rip), %rax      # 0x19480
movq (%rax), %rdi
callq 0xcf8b
movq 0x18(%rsp), %rdi
xorl %ebx, %ebx
leaq 0x70(%rsp), %rax
cmpq %rax, %rdi
jne 0xaa87
jmp 0xaa8c