Thread Interface

Serpent threads are unlike Python or Unix threads in that they are non-preemptable, meaning that threads run until a command explicitly allows another thread to run. Only one thread runs at once, even if multiple processors are available.

To create a thread, use fork(), which copies the current stack frame (the current function’s local variables and program counter). The original thread continues executing and the new thread starts executing as if both called fork(). The difference is that in the original thread, fork() returns the new thread (a reference to a thread primitive of type Thread, while in the new thread, fork() returns nil (false) and when this thread returns from the function where fork() was called, the thread terminates.

After creating a new thread with fork(), the calling thread yields to the new thread. You can also suspend threads to take them off the runnable list so they will not be yielded to (see suspend(), and you can make a suspended thread runnable again (see resume()).

A typical way to create a thread to perform some computation is:

    if not fork():
        some_computation() // runs on new thread
        return
    ... work for main thread continues ...

Alternatively, one can make a function that runs on a separate thread as follows:

def some_computation()
    if fork(): // create new thread for the work
        return // caller does none of the work
    ... some computation here for new thread ... 

One could also arrange things to defer computation until after the main thread suspends or yields control:

def some_computation()
    var caller = threadid() // get the calling thread
    if fork()
        return // caller will run this soon after fork()
    // after fork, new thread will run immediately,
    // so yield control back to calling thread
    yieldto(caller)
    // eventually, control will come back to this new thread:
    ... some defered computation here for new thread ... 

Procs – Preemptable Thread Interface and Functions

Serpent has two types of threads. “Normal” threads are non-preemptable coroutines that share the heap (thus all globals are common to these threads). You can create as many of these threads as you wish. The second form is limited to one additional thread (called a “process” or “proc” to avoid overloading the term “thread”) that loads a file and then periodically calls a function. This proc has an independent heap (thus no variables are shared) and runs at higher priority than the main proc. It can preempt the main proc or run while the main proc is blocked waiting for input or sleeping. The entire preemptive proc interface consists of proc_create()proc_send() and proc_receive().

All communication between these two procs is through message queues. Two queues are set up and initialized to hold up to 100 strings of up to 100 characters each. Only strings may be sent and received. To build Serpent with these proc functions, link Serpent with the objects obtained from proccreate.cpp and prochack.cpp. (Look for the CMake option USE_PROC to use procs.)

It is strongly recommended that you do not depend heavily on this simple proc interface. It was created to support a course and is not intended for “real” use.

proc_create(period, filename, mempool)
  • create a new instance of the Serpent virtual machine
  • allocate an initial memory pool (mempool is a hint for the size of the initial memory pool. It is currently ignored. Future implementations will interpret a value of 0 to indicate the default memory pool size, which is currently 1MB.)
  • load the file indicated by filename (a string)
  • set the variable proc_id in the new proc to an integer (see below)
  • return the new proc’s proc_id
  • Once a new proc is created and it has finished successfully loading/compiling/executing commands from filename, the proc uses PortTime to wake up every period milliseconds and call porttime_callback(ms), where ms is the current time in milliseconds.
proc_send(proc_id, string)
enqueue string for receipt by the other proc. Return the number of strings sent (0 if the queue is full, 1 if the send is successful.) proc_id should be the proc id of the caller, not the destination. (Use the global variable proc_id.
proc_receive(proc_id)
check the queue and if there is a message from the other proc, return the message as a string. If there is no message, “” (the empty string) is returned. Note that it is possible to send an empty string, but this will be indistinguishable from no message (an empty queue). proc_idshould be the proc id of the caller, not the proc that sent the message. (Use the global variable proc_id).

Network Interface and Functions

If Serpent is compiled with NETWORK defined, then some basic communications functions are built-in. They are defined in this section.

server_create(portno)
Create a socket, bind it to portno, and listen for client connections. A socket descriptor (number) is returned. -1 is returned to indicate an error.
server_accept(socket)
Accept a client request on socket, which was created by server_create. If the return value is nil, then no client request is pending (this is a non-blocking call). If the return value is negative, an error occurred. -1 indicates an error was reported from the accept operation. -2 indicates that the socket parameter value is invalid. (Other negative integers should also be treated as errors.) Otherwise, the return value is socket that can be used to read the client request. Under Windows, calling this function initiates a blocking accept call in another thread. In order to call server_connect or socket_receive, you must continue (re)calling server_accept until it returns something other than nil. To terminate the blocked accept, try closing the server socket and then re-calling server_accept to read the error return.
server_connect(name, portno)
Establish a connection with a server using its name and port number. The result is a socket, nil if no result is available yet (this is a non-blocking call), or -1 if there is an error. If nil is returned, you must re-call server_connect until a non-nil result is obtained.
socket_receive(socket, n)
Read up to n bytes of data from socket. Returns a string if successful, nil if no input is available (this is a non-blocking call), and otherwise returns an integer error code. The error code -2 is returned if the socket parameter is invalid. The socket is normally obtained from server_accept or server_connect. If nil is returned, the read is still in progress, and you must re-call socket_receive until a non-nil result is obtained.
socket_send(socket, string)
Send a string to the given socket, which is normally obtained from server_accept or server_connect. Returns the number of bytes sent or -1 on error.
socket_close(socket)
Close a socket.

Windows Shell File Operations

The Win32 version of Serpent includes an interface to “Shell File Operations” that perform tasks such as copying directories. These functions are:

sfo_copy_directory(from_path, to_path)
Copy a directory named by from_path to to_path (both arguments are strings).
sfo_delete(path)
Delete a file or directory named by path (a string).
create_directory(path)
Create a directory named by path (a string).
local_time()
Return the local time as an array of integers, organized as follows: [seconds, minutes, hours, day-of-month, month, year, day-of-week, day-of-year, dst], where dst is 1 for daylight savings time and 0 otherwise.
manasa on Emailmanasa on Githubmanasa on Linkedinmanasa on Twitter
manasa
Blockchain research analyst at Nvest Labs
Graduated as a Computer Science engineer from VTU in 2017. Currently pursuing my Masters in Software Engineering from University of Visvesvaraya College of Engineering (2017-19 batch). Interning as Blockchain research analyst at Nvest Labs.
WhatsApp chat