t/f = lock(path|fd[,type])
t/f = free(path|fd)
The lock() function is used to prevent other processes from modifying a regular file, at the specified path or FD, until it is released with a call to free(); or by a call to close() the file; or by termination of the process. A new Fing is automatically created if it doesn't already exist, even if the file itself does not exist, either. In the latter case, an empty file is created at the specified path, which is then locked before any I/O functions are called on the file. Without any I/O at all, this is a simple way to create and manage shared, empty, lock files.
The optional type argument is a single digit which specifies whether a read (1) or write (2) lock is to be applied (and defaults to a write lock, if omitted). Any number of processes may apply a read lock on the same file, because they are basically promising they will not write to the file as long as their lock is in effect. However, a read lock will not succeed if any other process has already acquired a write lock on the file. A write lock can be applied by only one process at a time and succeeds only if there are no other read or write locks, at all.
Normally, the lock() function returns immediately with true if the lock was acquired or false if a conflicting lock is already applied. Alternatively, a read lock may be requested with a type of (3) or a write lock with (4). In this case, the program is blocked until the requested lock can be obtained, or until the call is interrupted (for example, by a timer).
JSUS programs always respect the locks of other programs, even if they do not themselves use locking, and even if the locking program does not use JSUS. JSUS will not read any data which is even partly write-locked by another program. It will not write data if it overlaps any part of the file with either a read or write lock placed by another program. In these cases, the get() or put() will return null, with errno set to EBUSY (16).
CAUTION: for the reasons given below, file locking is only "advisory", never "mandatory". In other words, it works only between co-operating programs and processes. There is nothing to stop non-compliant or malicious programs from completely ignoring locks and just writing to the file whenever they choose. However, properly coded JavaScript programs, using these functions, should not have any problems safely interacting with each other. And, some level of protection from rogue programs can be provided using file "permissions" and access control lists (ACL's).
The file locking mechanisms available in all Unix-like systems, including Linux and BSD, all suck very bad eggs. For historical reasons, there are at least three different locking systems and they are all incompatible with each other. None of them can be guaranteed to work on files accessed over a network and, although Linux supposedly provides a "mandatory" locking option, it is documented to be "unreliable" and cannot be safely used, at all.
In JSUS we have chosen to use "fcntl" locking (without the "mandatory" option) which is the method chosen for the POSIX standard. This is the most portable, being available on all modern Unix-like systems. It also works with network file systems, provided they are configured correctly. However, there are a few quirks with which programmers should be familiar.
First, file locks are not inherited by a child process, after a call to fork() or run() (although they are inherited by the new program after a jump() within the same process). Therefore, after a fork(), a child process must always acquire its own locks.
Second, fcntl locks are applied to the physical file itself (the inode) and not to the file name or a particular file descriptor. (In Unix, the same physical file can be referenced by several different names, or "links"). If a system library function causes an already locked file to be opened, then, whenever that file is closed, all locks are released, whoever applied them, regardless of the name used to open the file. For example, if a JavaScript program reads and locks the "/etc/passwd" file, it will lose that lock if it calls getuser(). This happens because the lower level system function opens and then closes the same file, releasing the lock, even though it did not actually lock it! This is a serious design flaw but, fortunately, it doesn't often apply to normal JavaScript programs; it applies only within a single process; and it can be worked around.