Solaris portability - flock()
This is the first of what I hope will be a regular series of posts looking at
software portability when it comes to Solarish (Solaris, illumos, SmartOS etc.)
systems. We will begin with flock()
, as it’s what I happen to have been
fixing today.
The most recent version of tmux fails with the following errors:
from this section of code:
This code is reasonably straight-forward, trying to create a lock on a file descriptor, and if it isn’t able to waits until the lock is released then fails, ready for the calling function to retry.
However, the flock()
routine is of BSD heritage and does not exist on
Solaris. There used to be a compatability version as part of the /usr/ucb
environment, but that no longer exists in newer versions.
Thankfully, there is an alternative interface which has existed for as long as
flock()
but with the added benefit of being standardised by POSIX and thus
much more portable - enter fcntl()
!
Let’s look at how the two are similar-but-different, and then show how we can change this code to be more portable.
flock() implementation
As we can see from the NetBSD manual page below, flock()
is very simple, and
this is probably the main reason why people choose to use it over fcntl()
for
file locking.
There isn’t much to it, you call flock()
specifying either a shared or
exclusive lock, with an option to make the call non-blocking. Afterwards you
unlock the previously held lock.
The tmux code in question uses LOCK_NB
for the initial call, then if that
fails reverts to the default blocking operation so that the client can retry as
soon as the lock is released.
Use fcntl() instead!
In contrast to flock()
, setting up fcntl()
is a bit more involved as it
allows finer-grained control over the locking.
Set up struct flock
There is a flock
structure which controls the lock, defined as:
So, let’s set that up:
Setting l_start
and l_len
both to 0 means ‘lock the entire file’,
l_whence
of SEEK_SET
means we are setting absolute values rather than
relative, and we set l_type
to be a write lock. If we wanted a read lock,
we’d use F_RDLCK
here instead.
Note that l_pid
is only used for a F_GETLK
operation - we are only
interested in attempting to set a lock, so it is left unset.
Add fcntl() calls
The synopsis for fcntl()
is:
and the commands available for file locking are (from BSD):
Note there is no ‘clear lock’ command. To clear a lock, you use the F_GETLK
command with l_type
set to F_UNLCK
.
So, to show how we would rewrite the flock()
instances to fcntl()
instead,
I’ve put them together below:
As you can see, they are very similar, making it relatively straight-forward to
rewrite code to use the more portable fcntl()
.
An important note on semantics
There are two important difference between flock()
and fcntl()
you need to
be aware of which may affect a simple conversion:
-
fcntl()
locks are not held across afork()
, so you cannot pass locks down to child processes. -
The semantics of
fcntl()
are such that any closure of a file descriptor in your application will release the locks held against that file. This is best illustrated with a lock on/etc/passwd
that gets released if you callgetpwname()
as that opens and closes the/etc/passwd
file.
Generally such semantics do not apply, but you should be aware of them, and it always pays to carefully read the manual pages, preferably those from BSD.
flock() -> fcntl() cheat sheet
To aid your own conversions, here are some further examples (without error
checking for clarity) of flock()
and fcntl()
equivalents.
flock() wrapper
Alternatively, there is a flock()
wrapper which is used by the NetBSD
toolchain, which you could include in a compatability library or so:
It might be handy if the illumos folks merged this, it would immediate fix at least 19 packages in the pkgsrc collection :)
Summary
flock()
is simpler, and retains locks across fork()
and concurrent access
boundaries, but at the cost of portability. Please try to use fcntl()
where
possible, it isn’t much harder to use, and makes your software run on more
platforms.
All Posts
- 16 Jul 2015 » Reducing RAM usage in pkgin
- 03 Mar 2015 » pkgsrc-2014Q4: LTS, signed packages, and more
- 06 Oct 2014 » Building packages at scale
- 04 Dec 2013 » A node.js-powered 8-bit CPU - part four
- 03 Dec 2013 » A node.js-powered 8-bit CPU - part three
- 02 Dec 2013 » A node.js-powered 8-bit CPU - part two
- 01 Dec 2013 » A node.js-powered 8-bit CPU - part one
- 21 Nov 2013 » MDB support for Go
- 30 Jul 2013 » What's new in pkgsrc-2013Q2
- 24 Jul 2013 » Distributed chrooted pkgsrc bulk builds
- 07 Jun 2013 » pkgsrc on SmartOS - creating new packages
- 15 Apr 2013 » What's new in pkgsrc-2013Q1
- 19 Mar 2013 » Installing SVR4 packages on SmartOS
- 27 Feb 2013 » SmartOS is Not GNU/Linux
- 18 Feb 2013 » SmartOS development preview dataset
- 17 Jan 2013 » pkgsrc on SmartOS - fixing broken builds
- 15 Jan 2013 » pkgsrc on SmartOS - zone creation and basic builds
- 10 Jan 2013 » Multi-architecture package support in SmartOS
- 09 Jan 2013 » Solaris portability - cfmakeraw()
- 08 Jan 2013 » Solaris portability - flock()
- 06 Jan 2013 » pkgsrc-2012Q4 illumos packages now available
- 23 Nov 2012 » SmartOS and the global zone
- 24 Oct 2012 » Setting up Samba on SmartOS
- 10 Oct 2012 » pkgsrc-2012Q3 packages for illumos
- 23 Aug 2012 » Creating local SmartOS packages
- 10 Jul 2012 » 7,000 binary packages for OSX Lion
- 09 Jul 2012 » 9,000 packages for SmartOS and illumos
- 07 May 2012 » Goodbye Oracle, Hello Joyent!
- 13 Apr 2012 » SmartOS global zone tweaks
- 12 Apr 2012 » Automated VirtualBox SmartOS installs
- 30 Mar 2012 » iptables script for Debian / Ubuntu
- 20 Feb 2012 » New site design
- 11 Jan 2012 » Set up anonymous FTP upload on Oracle Linux
- 09 Jan 2012 » Kickstart Oracle Linux in VirtualBox
- 09 Jan 2012 » Kickstart Oracle Linux from Ubuntu
- 22 Dec 2011 » Last day at MySQL
- 15 Dec 2011 » Installing OpenBSD with softraid
- 21 Sep 2011 » Create VirtualBox VM from the command line
- 14 Sep 2011 » Creating chroots for fun and MySQL testing
- 30 Jun 2011 » Graphing memory usage during an MTR run
- 29 Jun 2011 » Fix input box keybindings in Firefox
- 24 Jun 2011 » How to lose weight
- 23 Jun 2011 » How to fix stdio buffering
- 13 Jun 2011 » Serving multiple DNS search domains in IOS DHCP
- 13 Jun 2011 » Fix Firefox URL double click behaviour
- 20 Apr 2011 » SSH via HTTP proxy in OSX
- 09 Nov 2010 » How to build MySQL releases
- 29 Apr 2010 » 'apt-get' and 5,000 packages for Solaris10/x86
- 16 Sep 2009 » ZFS and NFS vs OSX
- 12 Sep 2009 » pkgsrc on Solaris
- 09 Dec 2008 » Jumpstart from OSX
- 31 Dec 2007 » Set up local caching DNS server on OSX 10.4