def emit_endpoint_global_and_peered_variable_store( endpoint_name,host_uuid_var_name,ast_root,fdep_dict,emit_ctx): ''' For params, @see emit_single_endpoint Returns a string that initializes the global variable store contained in self._global_var_store. ''' endpoint_global_decl_nodes = get_endpoint_global_decl_nodes( endpoint_name,ast_root) peered_decl_nodes = get_peered_decl_nodes(ast_root) var_store_loading_text = '_active_event = None' var_store_loading_text += ''' _context = %s( self._global_var_store, # not using sequence local store %s(_host_uuid)) ''' % ( emit_utils.library_transform('ExecutingEventContext'), emit_utils.library_transform('VariableStore')) var_store_loading_text += create_wvariables_array( host_uuid_var_name,endpoint_global_decl_nodes,False, endpoint_name,ast_root,fdep_dict,emit_ctx) var_store_loading_text += create_wvariables_array( host_uuid_var_name,peered_decl_nodes,True, endpoint_name,ast_root,fdep_dict,emit_ctx) return var_store_loading_text
def emit_endpoint_init( endpoint_name,ast_root,fdep_dict,emit_ctx): ''' For params and return, @see emit_single_endpoint ''' oncreate_argument_string = '' oncreate_node = get_oncreate_node(endpoint_name,ast_root) if oncreate_node != None: oncreate_arg_names = get_method_arg_names(oncreate_node) oncreate_argument_string = reduce ( lambda x, y : x + ',' + y, oncreate_arg_names,'') init_header = ( 'def __init__(self,_waldo_classes,_host_uuid,_conn_obj%s):\n' % oncreate_argument_string) # create endpoint and peered variable store # should initialize variable store before entering into _Endpoint # super class initializer: super class initializer registers the # _Endpoint with connection object. endpoint_global_and_peered_variable_store_load_txt = ( emit_endpoint_global_and_peered_variable_store( endpoint_name,'_host_uuid',ast_root,fdep_dict,emit_ctx)) # actually initialize super class init_body = ''' # a little ugly in that need to pre-initialize _host_uuid, because # code used for initializing variable store may rely on it. (Eg., if # initializing nested lists.) self._waldo_classes = _waldo_classes self._host_uuid = _host_uuid self._global_var_store = %s(_host_uuid) %s %s.__init__(self,_waldo_classes,_host_uuid,_conn_obj,self._global_var_store) ''' % (emit_utils.library_transform('VariableStore'), endpoint_global_and_peered_variable_store_load_txt, emit_utils.library_transform('Endpoint')) # emit call to oncreate method init_body += emit_oncreate_call(endpoint_name,ast_root) return init_header + emit_utils.indent_str(init_body)
def emit_message_node_what_to_call_next(next_to_call_node,emit_ctx): ''' @param {AstNode or None} next_to_call_node --- A message receive node or None. At the end of a message send or message receive function, execution should "fall through" to the following message receive block. This function emits the code that handles this logic. Ie, it requests active event to issue a request to execute a message sequence block. (Note: if next_to_call_node is None, then that means that we were at the end of a sequence and do not have any other blocks to execute: return empty string. ''' if next_to_call_node == None: return '' # FIXME: seems to reproduce a lot of work already done in # emit_message_receive. next_message_name_node = next_to_call_node.children[1] next_message_name = next_message_name_node.value issue_call_is_first_txt = 'False' if emit_ctx.in_message_send: # allows us to determine if this is the first message in a # sequence block that is being sent. If it is, then we need # to force sequence local data to be synchronized. If it's # not, we only need to synchronize modified sequence local data. issue_call_is_first_txt = '_first_msg' return ''' _threadsafe_queue = %s.Queue() _active_event.issue_partner_sequence_block_call( _context,'%s',_threadsafe_queue, '%s') _queue_elem = _threadsafe_queue.get() if isinstance(_queue_elem,%s): raise %s() _context.set_to_reply_with(_queue_elem.reply_with_msg_field) # apply changes to sequence variables. (There shouldn't # be any, but it's worth getting in practice.) Note: that # the system has already applied deltas for global data. _context.sequence_local_store.incorporate_deltas( _active_event,_queue_elem.sequence_local_var_store_deltas) # send more messages _to_exec_next = _queue_elem.to_exec_next_name_msg_field if _to_exec_next != None: # means that we do not have any additional functions to exec _to_exec = getattr(self,_to_exec_next) _to_exec(_active_event,_context) else: # end of sequence: reset to_reply_with_uuid in context. we do # this so that if we go on to execute another message sequence # following this one, then the message sequence will be viewed as # a new message sequence, rather than the continuation of a # previous one. _context.reset_to_reply_with() ''' % (emit_utils.library_transform('Queue'), next_message_name, # the name of the message receive func to # exec on other side in plain text issue_call_is_first_txt, emit_utils.library_transform('BackoutBeforeReceiveMessageResult'), emit_utils.library_transform('BackoutException'), )
def emit_message_receive( message_receive_node,next_to_call_node,endpoint_name,ast_root, fdep_dict,emit_ctx): ''' @param {AstNode or None} next_to_call_node --- If AstNode, then has label AST_MESSAGE_RECEIVE_FUNCTION. If None, means that this is the last message receive node in sequence and should tell other side to issue a block call with target None. ''' msg_recv_node_name_node = message_receive_node.children[1] msg_recv_name = msg_recv_node_name_node.value emit_ctx.in_message_receive = True emit_ctx.message_seq_return_txt = '\nreturn ' msg_receive_txt = emit_private_method_interface( message_receive_node,endpoint_name,ast_root,fdep_dict,emit_ctx, lib_util.partner_endpoint_msg_call_func_name) msg_receive_txt += '\n' emit_ctx.in_message_receive = False emit_ctx.message_seq_return_txt = '' ## Ends by telling the opposite side what to do next next_to_call_txt = 'None' if next_to_call_node != None: # the name of the next sequence block to execute, as it # appears in the source Waldo text, ie, before mangling. next_sequence_block_src_name = next_to_call_node.children[1].value next_to_call_txt = '"' + lib_util.partner_endpoint_msg_call_func_name( next_sequence_block_src_name) + '"' # note that we must wait on receiving a response from the other # side before we can continue on, or the original sender may # return too early. We should not wait on the last message that we # send however, because, we do not expect any response to it. # (Ie, if next_to_call_txt == 'None', then do not wait.) next_sequence_txt = ''' _threadsafe_queue = %s.Queue() _active_event.issue_partner_sequence_block_call( _context,%s,_threadsafe_queue,False) # must wait on the result of the call before returning if %s != None: # means that we have another sequence item to execute next _queue_elem = _threadsafe_queue.get() ''' % (emit_utils.library_transform('Queue'), next_to_call_txt, next_to_call_txt) next_sequence_txt += ''' if isinstance(_queue_elem,%s): # back everything out raise %s() _context.set_to_reply_with(_queue_elem.reply_with_msg_field) # apply changes to sequence variables. Note: that # the system has already applied deltas for global data. _context.sequence_local_store.incorporate_deltas( _active_event,_queue_elem.sequence_local_var_store_deltas) # send more messages _to_exec_next = _queue_elem.to_exec_next_name_msg_field if _to_exec_next != None: # means that we do not have any additional functions to exec _to_exec = getattr(self,_to_exec_next) _to_exec(_active_event,_context) ''' % (emit_utils.library_transform('BackoutBeforeReceiveMessageResult'), emit_utils.library_transform('BackoutException')) msg_receive_txt += emit_utils.indent_str(next_sequence_txt) return msg_receive_txt
def emit_public_method_interface( public_method_node,endpoint_name,ast_root,fdep_dict,emit_ctx): ''' @param {AstNode} public_method_node --- An AstNode with label AST_PUBLIC_FUNCTION ''' method_name_node = public_method_node.children[0] method_name = method_name_node.value method_arg_names = get_method_arg_names(public_method_node) # turns the array of argnames above into a single string of csv # arg names comma_sep_arg_names = reduce ( lambda x, y : x + ',' + y, method_arg_names,'') public_header = ''' def %s(self%s): ''' % (method_name, comma_sep_arg_names) # wait until ready initialization for node has completed before # continuing public_body = ''' # ensure that both sides have completed their onCreate calls # before continuing self._block_ready() ''' #### Deep copy non-external args # non_ext_arg_names is an array of strings non_ext_arg_names = get_non_external_arg_names_from_func_node( public_method_node) # do not need to copy arguments in: each function call does so on # its own. # Each element in this list is an index for a return parameter # that should be de-waldo-ified before returning. We pass this # argument to the internal function that the internal function # knows which returns need to be de-waldo-ified before being # returned and which do not. list_return_external_positions = ( get_external_return_positions_from_func_node(public_method_node)) #### create a root event + ctx for event, call internal, and reurn internal_method_name = lib_util.endpoint_call_func_name(method_name) public_body += ''' while True: # FIXME: currently using infinite retry _root_event = self._act_event_map.create_root_event() _ctx = %s( self._global_var_store, # not using sequence local store %s(self._host_uuid)) # call internal function... note True as last param tells internal # version of function that it needs to de-waldo-ify all return # arguments (while inside transaction) so that this method may # return them....if it were false, might just get back refrences # to Waldo variables, and de-waldo-ifying them outside of the # transaction might return over-written/inconsistent values. _to_return = self.%s(_root_event,_ctx %s,%s) # try committing root event _root_event.request_commit() _commit_resp = _root_event.event_complete_queue.get() if isinstance(_commit_resp,%s): # means it isn't a backout message: we're done return _to_return ''' % (emit_utils.library_transform('ExecutingEventContext'), emit_utils.library_transform('VariableStore'), internal_method_name, comma_sep_arg_names, str(list_return_external_positions), emit_utils.library_transform('CompleteRootCallResult')) return public_header + emit_utils.indent_str(public_body)
def emit_oncreate_call(endpoint_name,ast_root): ''' Inside of each endpoint's initializer, we may need to emit a call to an onCreate initializer method. Below emits the text that actually calls the initializer method. @returns {String} ''' oncreate_node = get_oncreate_node(endpoint_name,ast_root) if oncreate_node != None: oncreate_arglist = get_method_arg_names(oncreate_node) comma_sep_arg_names = reduce ( lambda x, y : x + ',' + y, oncreate_arglist,'') # run the initializer oncreate_call_txt = ''' while True: # FIXME: currently using infinite retry _root_event = self._act_event_map.create_root_event() _ctx = %s( self._global_var_store, # not using sequence local store %s(self._host_uuid)) # call internal function... note True as last param tells internal # version of function that it needs to de-waldo-ify all return # arguments (while inside transaction) so that this method may # return them....if it were false, might just get back refrences # to Waldo variables, and de-waldo-ifying them outside of the # transaction might return over-written/inconsistent values. _to_return = self.%s(_root_event,_ctx %s,[]) # try committing root event _root_event.request_commit() _commit_resp = _root_event.event_complete_queue.get() if isinstance(_commit_resp,%s): # means it isn't a backout message: we're done # local endpoint's initialization has succeeded, tell other side that # we're done initializing. self._this_side_ready() return _to_return ''' % (emit_utils.library_transform('ExecutingEventContext'), emit_utils.library_transform('VariableStore'), lib_util.internal_oncreate_func_call_name('onCreate'), comma_sep_arg_names, emit_utils.library_transform('CompleteRootCallResult')) else: oncreate_call_txt = '\n' # local endpoint's initialization has succeeded, tell other side # that we're done initializing. note: we don't hit this section # of code if we actually had an oncreate node, so it's okay that # we list self._this_side_ready() twice: only one of them will # ever be called. oncreate_call_txt += ''' # local endpoint's initialization has succeeded, tell other side that # we're done initializing. self._this_side_ready() ''' return oncreate_call_txt