MFP language call and endcall statements:
The call and endcall statements define the boundary of a MFP call block which will be executed in a new thread of the current process, a different process in local host or a remote process in a different host. The call statement begins a call block. It is followed by a local keyword if the thread of call block runs in the current process; or a connection object, then on keyword, then the call statement parameter variables if the call block runs in a different process whether remote or local. The call block parameter variables are normal variables declared before the call block. Each variable can only be used by one call block. The endcall statement ends a call block. It has an optional parameter which is returned variable. The returned variable is a normal variable declared before the call block. It cannot be in the call statement parameter variable list and cannot be used by any other call statement or endcall statement.
Basically a call statement works like a function but runs in a different thread. The current thread which triggers the call statement simply starts the call block in a new thread if local keyword is used in the call statement. Otherwise, the call block is sent to a remote process which is connected to the local process by the connection object included in the call statment. Like running in the current process, a call block running in a different process can see the snapshot of values of all the variables in the current process at the moment when the call block starts. However, a call block running in the current process can modify any visible variables (except the endcall returned variable), while a call block running in a different process can only update the values of the call statement parameter variables. If a call block runs in a different process, the current process can see all the updates of the call statement parameter variables made by the call block but MFP doesn't do real-time sync nor guarantee the sync order. Similarly, the current process can also update values of the call statement parameter variables. The remote call block can see all the updates but MFP doesn't do real-time sync nor guarantee the sync order. MFP can only guarantee that one update of a variable in one process is atomic. Please note that this atomic feature only exists inside process. Since remote process and the current process both have a copy of call statement parameter variables, it is valid that the two copies of a call block variable are updated in different processes simultaneously.
A call block finishes its running when a return statement is hit or when the call block ends. If the return statement running in the call block does return a value, the endcall statment in the current thread will receive the returned value from the call block thread. If the endcall statement includes a returned variable name, the value of the returned variable will be updated by the received returned value.
Please note that, different from the parameter variables in the call statement, the returned variable included in the endcall statement is a blocked variable. This means that any statement trying to use its value will be blocked until the call block returns (It doesn't matter if the call block returns a value or not).
An example of local call block, i.e. starting a new thread, is shown below:
variable a = 3, b = 4 //Because the call block starts a new thread in the current process, every variable, //except the endcall returned variable, is readable and writable. As such no need to //use on keyword and declare call block parameters. call local a = "HELLO" suspend_until_cond(a) // suspend call block thread until variable a's value is changed. sleep(1000) // sleep 1 second to ensure that the thread which launches this call block can arrive at suspend_until_cond b = 24 // set b value 24 so that the thread which launches this call block can continue endcall sleep(1000) // sleep 1 second to ensure that call block thread can arrive at suspend_until_cond a = 9 //change variable a's value so that call block thread can continue suspend_until_cond(b, false, "==", 24) //block current thread at variable b. If b's value is updated to 24 then continue print_line("a = " + a + " b = " + b) //Now a and b have been updated in the call block. The current thread can see the updated values
Another example is for inter-process call block, i.e. call block is sent to run in a different process in local or a remote machine:
variable local_interface, remote_interface, ret local_interface = ::mfp::paracomp::connect::generate_interface("TCPIP", "192.168.1.101") // client address ret = ::mfp::paracomp::connect::initialize_local(local_interface) print("initialize_local ret = " + ret + "\n") remote_interface = ::mfp::paracomp::connect::generate_interface("TCPIP", "192.168.1.107") // server address ret = ::mfp::paracomp::connect::connect(local_interface, remote_interface) // connect to server from client print("connect ret = " + ret + "\n") // return of connect function is a dictionary. The value of "CONNECT" key is the connection object definition // if connect failed, the value of "CONNECT" key is NULL. variable conn = ::mfp::data_struct::array_based::get_value_from_abdict(ret, "CONNECT") variable a = "hekko, 48", b = 3+7i, c=["LCH"] variable d = 27 // variable d is used as a lock for synchronization between call block thread and the current thread call conn on a, b, d // only updates of variables a, b and d in the call block thread can be seen by the current thread print("Before suspend_until_cond(d, false, \"==\", 888), d = " + d + "\n") suspend_until_cond(d, false, "==", 888) // block until d's value is updated to 888 print("After suspend_until_cond(d, false, \"==\", 888), d = " + d + "\n") sleep(5000) //sleep 5 seconds to ensure that the thread which launches this call block can arrive at suspend_until_cond d = 213 // change d's value. The thread that launches this call block will receive the new value and then it can resume //sleep the call block thread again. Now the thread which launches this call block should //have arrived at print_line("c = " + c). Because the call block hasn't returned, the thread which //launches this call block cannot read c's value so it is blocked again. sleep(5000) a = 88 b = "KIL" return 54 endcall c sleep(10000) //sleep 10 seconds to wait for call block thread to start and arrive at suspend_until_cond d = 888 //change d's value to 888. After d's new value is received by the call block, it can continue suspend_until_cond(d) // block the current thread waiting for change of d's value print_line("New value of d is " + d) //when we can get back c's value, call block must have returned print_line("c = " + c) // the current thread is blocked here. It will continue after c's new value is returned from the call block // only after value of c has been retrieved, we can print a and b's values and we can see // that they have been updated. If we print a and b's values before print("c = " + c), we // MAY see a and b are not updated. print("a = " + a + " b = " + b) close_connection(conn) // close connection close_local(local_interface) // close local interface
The above code is for the client process. In the server side, we have to run the following code to accept connection and run call block:
variable local_interface, ret local_interface = ::mfp::paracomp::connect::generate_interface("TCPIP", "192.168.1.107") // server address ret = ::mfp::paracomp::connect::initialize_local(local_interface) print("initialize_local ret = " + ret + "\n") // listen to any connection request. A listen thread will work in the background ret = ::mfp::paracomp::connect::listen(local_interface) print("listen ret = " + ret + "\n") // This input statement prevents server to quit if server code is a simple MFPS script // and runs from bash or Windows command line. In Android or MFP JAVA GUI, the following // input statement is not required as long as Scientific Calculator Plus app / MFP // JCmdline program is still running, because server is not terminated anyway. input("Press any key to exit\n", "S")
Start server side code first and then run client side code in a different device. Make sure server address and client address are both correct. In the server side two messages are printed. One is Before suspend_until_cond(d, false, "==", 888), d = 27, the other is After suspend_until_cond(d, false, "==", 888), d = 888. In the client side variables a, b, c and d's new values are printed. In particular, variable c is returned as a dictionary with returned value, 54, included.