about summary refs log tree commit diff
path: root/doc/libs6/s6lock.html
blob: ca22fe4a3dec787880c2cf074b8979d569f72648 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Content-Language" content="en" />
    <title>s6: the s6lock library interface</title>
    <meta name="Description" content="s6: the s6lock library interface" />
    <meta name="Keywords" content="s6 timed lock s6lock libs6 library interface" />
    <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
  </head>
<body>

<p>
<a href="index.html">libs6</a><br />
<a href="../">s6</a><br />
<a href="http://skarnet.org/software/">Software</a><br />
<a href="http://skarnet.org/">skarnet.org</a>
</p>

<h1> The <tt>s6lock</tt> library interface </h1>

<h2> General information </h2>

<p>
 <tt>s6lock</tt> is a C interface to timed locks. Unix natively provides
locks, but the locking primitives are synchronous, so either they are
unbounded in execution time or they require polling. s6lock provides
poll-free locks that can timeout during attempted acquisition.
</p>

<h2> Programming </h2>

<ul>
 <li> Check the <tt>s6/s6lock.h</tt> header
for the prototypes. The functions documented here are
often simplified macros, for instance relying on the STAMP global variable
to hold the current time. Fully reentrant functions with more control
options are usually available. </li>
 <li> Given the nature of the s6lock library, it makes sense to use a
<a href="../localservice.html">s6lockd service</a> concurrently
accessed by several applications using such locks to gate shared
resources. </li>
 <li> If you're not using a s6lockd service,
make sure your application is not disturbed by children it doesn't
know it has. Using nonblocking waits, ignoring pids you don't know, and
using a
<a href="http://skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a>
if your application is built around an event loop, are good programming
practices. </li>
</ul>

<h3> Starting and ending a session </h3>

<pre>
s6lock_t a = S6LOCK_ZERO ;
tain_t deadline ;

tain_now_g() ;
tain_addsec_g(&amp;deadline, 2)

char const *path = S6LOCK_IPCPATH ;
s6lock_start_g(&amp;a, path, &amp;deadline) ;
// char const *lockdir = "/tmp/lock" ;
// s6lock_startf_g(&amp;a, lockdir, &amp;deadline) ;
</pre>

<p>
<tt>s6lock_start_g</tt> starts a session by connecting to a s6lockd service
listening on <em>path</em>. The working directory is set by the administrator
of the service. <br />
<tt>s6lock_startf_g</tt> starts a session with a s6lockd process as a child,
using <em>lockdir</em> as its working directory.
<br />
<tt>a</tt> is a s6lock_t structure that must be declared in the stack and
initialized to S6LOCK_ZERO.
If the session initialization fails, the function returns 0 and errno is set;
else the function returns 1.
</p>
<p>
If the absolute time <tt>deadline</tt> is reached and the function
has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT.

Only local interprocess communications are involved; unless your system is
heavily overloaded, the function should return near-instantly. One or two
seconds of delay between the current time and <tt>deadline</tt> should be
enough: if the function takes more than that to return, then there is a
problem with the underlying processes.
</p>

<p>
 You can have more than one session open in parallel, by declaring
several distinct <tt>s6lock_t</tt> structures and calling
<tt>s6lock_startf_g</tt> (or <tt>s6lock_start_g</tt>) more than once.
However, one single session can handle
virtually as many concurrent locks as your application needs, so
opening several sessions is only useful if you need to acquire locks
in various distinct lock directories.
</p>

<pre>
s6lock_end(&amp;a) ;
</pre>

<p>
<tt>s6lock_end</tt> frees all the resources used by the session. The
<tt>a</tt> structure is then reusable for another session.
</p>

<h3> Acquiring and releasing locks </h3>

<pre>
uint16 id ;
char const *file = "lockfile" ;
tain_t limit ;
tain_t deadline ;

int r = s6lock_acquire_sh_g (&amp;a, &amp;id, file, &amp;limit, &amp;deadline) ;
/* int r = s6lock_acquire_ex_g (&amp;a, &amp;id, file, &amp;limit, &amp;deadline) ; */
r = s6lock_release_g(&amp;a, id, &amp;deadline) ;
</pre>

<p>
<tt>s6lock_acquire_sh_g</tt> instructs the
<a href="s6lockd.html">s6lockd daemon</a>, related to the open
session represented by the <tt>a</tt> handle, to try and acquire a
shared lock on the
<em>file</em> file located under that daemon's working directory
(typically <tt>/var/lock</tt>). <em>file</em> will be interpreted as
relative to the daemon's working directory even if it starts with a
slash; however, slashes in the middle of <em>file</em> are likely to
result in an error.
</p>

<p>
<em>limit</em> and <em>deadline</em> are two absolute dates.
<em>deadline</em> is a deadline for the execution of the
function: if by <em>deadline</em> the function has not returned,
then it instantly returns 0 and sets errno to ETIMEDOUT. The
function is normally near-instantaneous, so <em>deadline</em> can
be very close in the future and serves only as a protection against
malicious servers. <em>limit</em> is the acquisition deadline: if
by <em>limit</em> the daemon still has not been able to acquire a lock
on <em>file</em>, then it will report a timeout to the client.
</p>

<p>
The function returns 1 in case of success, or 0 if an error occurs,
with errno set to a suitable value. If it succeeds, then a 16-bit
number is stored into *<em>id</em>; this number serves as an identifier
for this lock.
</p>

<p>
<tt>s6lock_acquire_ex_g</tt> works just like <tt>s6lock_acquire_sh_g</tt>,
except that the daemon tries to acquire an exclusive lock.
</p>

<p>
<tt>s6lock_release_g</tt> releases the lock identified by <em>id</em>.
It normally returns 1. It can return 0 with errno set to a suitable
value if it fails. <em>id</em> is not valid after the corresponding
lock has been released. The function normally returns instantly, with
<em>deadline</em> as a safeguard.
</p>

<h3> Asynchronously waiting for locks </h3>

<p>
<em> (from now on, the functions are listed with their prototypes instead
of usage examples.) </em>
</p>

<pre>
int s6lock_fd (s6lock_t const *a)
</pre>

<p>
 Returns a file descriptor to select on for reading. Do not
<tt>read()</tt> it though.
</p>

<pre>
int s6lock_update (s6lock_t *a)
</pre>

<p>
 Call this function whenever the fd checks readability: it will
update <em>a</em>'s internal structures with information from the
<a href="s6lockd.html">s6lockd</a> daemon. It returns -1 if an error
occurs; in case of success, it returns the number of identifiers for
which something happened.
</p>

<p>
 When <tt>s6lock_update</tt> returns,
<tt>genalloc_s(uint16, &amp;a-&gt;list)</tt> points to an array of
<tt>genalloc_len(uint16, &amp;a-&gt;list)</tt> 16-bit unsigned
integers. Those integers are ids waiting to be passed to
<tt>s6lock_check</tt>.
</p>

<pre>
int s6lock_check (s6lock_t *a, uint16 id, char *what)
</pre>

<p>
 Checks whether the lock identified by <em>id</em> has
been acquired. Use after a call to <tt>s6lock_update()</tt>.
</p>

<ul>
 <li> If an error occurred, returns -1 and sets errno. The error
number may have been transmitted from
<a href="s6lockd.html">s6lockd</a>. </li>
 <li> If the lock has not been acquired yet, returns 0. </li>
 <li> If the lock has been acquired, returns 1. </li>
</ul>

<h3> Synchronously waiting for locks </h3>

<p>
<code> int s6lock_wait_or_g (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline) </code> <br />
Synchronously waits for <em>one</em> of the locks represented by the array pointed to
by <em>idlist</em> of length <em>n</em> to be acquired. Returns -1 if it fails,
or a nonnegative number on success, which is the index in <em>idlist</em> of the
acquired lock's id. If no result has been obtained by <em>deadline</em>, the
function returns -1 ETIMEDOUT.
</p>

<p>
<code> int s6lock_wait_and_g (s6lock_t *a, uint16 const *idlist, unsigned int n, tain_t const *deadline) </code> <br />
Synchronously waits for <em>all</em> of the locks represented by the array pointed to
by <em>idlist</em> of length <em>n</em> to be acquired. Returns -1 if it fails and
0 if it succeeds. If no result has been obtained by <em>deadline</em>, the
function returns -1 ETIMEDOUT.
</p>

</body>
</html>