• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

bazel/H25-Jun-2020-895809

builds/H25-Jun-2020-5,4705,245

internal/H25-Jun-2020-3,3882,109

platform/H25-Jun-2020-6,2863,303

public/H25-Jun-2020-1,170364

testing/H25-Jun-2020-6,2964,522

tools/H25-Jun-2020-1,4161,129

BUILDH A D25-Jun-202024.9 KiB886802

CONTRIBUTINGH A D25-Jun-2020998 2420

LICENSEH A D25-Jun-202011.1 KiB203169

READMEH A D25-Jun-20209.7 KiB249209

VERSIONH A D25-Jun-20207 21

README

1nsync is a C library that exports various synchronization primitives:
2	locks
3	condition variables
4	run-once initialization
5	waitable counter (useful for barriers)
6	waitable bit (useful for cancellation, or other conditions)
7
8It is not an offical Google product.
9
10nsync may be desirable in place of pthread primitives in some cases:
11- nsync locks are reader-writer locks (but are as efficient as mutexes).
12- nsync locks and condition variables occupy only two words each.
13- nsync works on Unix-like systems and Windows.  It should be portable to
14  other platforms straightforwardly.
15- nsync provides conditional critical sections.  These fill the same role
16  as condition variables, but are usually easier to use, and in most common
17  cases are comparable in speed.  They can be easier to use in two ways:
18  - it's not necessary to surround the "wait" operation in a while loop;
19    instead the condition is passed to the call as a function and arbitrary
20    pointer argument.
21  - it's not necessary to wake or signal explicitly when the condition(s)
22    become true; they are checked automatically.
23  The primary downsides are:
24  - they are not available in most other common synchronization APIs, and so
25    they may be unfamiliar (even though they date back to the 1960s), and
26  - if threads routinely wait on many distinct, false conditions
27    associated with the same lock, they may be slower than condition variables.
28    In this case, clients can use condition variables in the normal way;
29    conditional critical sections and condition variables can be used with
30    the same lock.
31- nsync waits can be cancelled via an object passed to the wait calls, unlike
32  the pthread model in which threads are cancelled.  This difference can be
33  useful if the computation needs multiple threads, or if cancellation affects
34  only sub-operations within a larger operation by the thread.
35See the section "Extensions to locks and condition variables" below.
36
37Portability
38===========
39The library is intended to be portable, and to be compilable on a system with
40only a C90 compiler, assuming atomic operations are available from the
41compiler, operating system, or assembler routines.  It is able to use C11 or
42C++11 atomic operations if they are available.
43It can be compiled with a C++ compiler, and in its own C++ name space, if
44desired, though no attempt has been made to present a class-based interface.
45
46Building
47========
48The builds/ directory may already contain a subdirectory that
49matches your platform.   For example, if you're on an x86_64,
50running Linux, using gcc, you might pick "x86_64.linux.gcc".
51
52If there is an appropriate subdirectory, in that subdirectory type:
53	make depend test
54which will calculate dependencies, build the library and its tests, and then
55run them.  (On Windows, using Visual Studio ("x86_64.win32.msvc")
56use "nmake" instead of "make".)
57
58If there is no suitable subdirectory, on most Unix-like systems you can create
59one with
60	tools/mkmakefile.sh
61
62The main reason it might fail is if it cannot find a suitable implementation of
63atomic operations on the platform.  Atomic operations may be provided by
64- compiler-dependent interfaces (currently, gcc and clang)
65  These are auto detected by mkmakefile.sh.
66- language-specific standards (currently, C11 and C++11)
67  Selected in mkmakefile.sh via "-atomic c11" or  "-atomic c++11".
68- operating system-dependent libraries (e.g., NetBSD, MacOS, Windows)
69  Selected in mkmakefile.sh via "-atomic os".
70- architecture-dependent libraries (e.g., x86_64, x86_32, aarch64,
71  arm, mips, alpha)
72  Selected in mkmakefile.sh via "-atomic asm"; file should be
73  named platforms/<architecture>/src/nsync_atm_<architecture>.[csS]
74  to be found by mkmakefile.sh.
75If none of these match your platform, you may need to provide
76an assembly language implementation.
77
78Other possible issues:
79- Some platforms put clock_gettime() in the realtime library.
80  Give "-lrt" to mkmakefile.sh.
81- The version identifier of "clang" can vary by installation,
82  and so it may not be identified if invoked as "cc".
83  Give "-cc clang" to mkmakefile.sh, if clang is not detected automatically.
84- Some CPU architectures have many variants, making it
85  difficult to rely on a single identifier.
86  Give "-arch <architecture>" to mkmakefile.sh to specify
87  a particular string.
88
89mkmakefile.sh recognises a couple of special cases:
90- MacOS doesn't provide clock_gettime(); a compatibility routine
91  is found in platform/posix/src/clock_gettime.c
92  See builds/x86_64.macos.clang/Makefile
93- OpenBSD and Irix do not provide thread-local storage, which is accommodated
94  by adding -I../../platform/gcc_no_tls to the include path.
95  See, for example,  builds/x86_64.openbsd.gcc/Makefile.
96
97Further customization is possible by editing the Makefile, directly.
98For Unix-like systems is typically only a few lines long.
99For example, compare
100	builds/x86_64.linux.g++/Makefile
101with
102	builds/x86_64.linux.gcc/Makefile
103to see how to compile the entire library in C++, rather than C.
104
105CMake
106-----
107
108CMake can also be used to build:
109
110    $ mkdir out
111    $ cd out/
112    $ cmake ..
113    $ make
114    $ make install
115
116The C library will be called libnsync and C++ is libnsync_cpp.
117
118Tests can be disabled with the CMake option: -DNSYNC_ENABLE_TESTS=0.
119To build shared libraries instead of static use: -DBUILD_SHARED_LIBS=ON.
120
121CMake version >= 3.0 is strongly recommended.
122
123Code structure
124==============
125public/		Public header files for library.
126builds/*/	Platform-dependent build directories, each with Makefile.
127internal/	Platform-independent library source code, and Makefile fragment.
128platform/*/	Platform-dependent source code.
129testing/	Platform-independent testing source code is in "testing".
130tools/		Optional tools that can be used to create Makefile
131		dependencies and run tests.
132
133Where possible, the code avoids conditional compilation (#if, etc.),
134to avoid becoming a mess of C-preprocessor directives.
135The platform-dependent Makefiles set the appropriate include
136paths and specify platform-dependent modules where needed.
137
138The build directories of the various platforms are kept separate to allow
139multiple platforms to be accommodated in one shared file system.
140
141Differences from pthread locks and condition variables
142======================================================
143
144Conditional critical sections
145-----------------------------
146Consider the following use of a condition variable:
147	/* variable declarations */
148	nsync_mu mu = NSYNC_MU_INIT;  /* protects i */
149	int i = 1;
150	nsync_cv cv = NSYNC_CV_INIT;  /* signalled when i reaches 0 */
151
152	...
153
154	/* Waiter */
155	nsync_mu_lock (&mu);
156	while (i != 0) {
157		nsync_cv_wait (&cv, &mu);
158	}
159	/* i is zero ... */
160	nsync_mu_unlock (&mu);
161
162	...
163
164	/* Decrementer */
165	nsync_mu_lock (&mu)
166	i--;
167	if (i == 0) {
168		nsync_cv_broadcast (&cv);
169	}
170	nsync_mu_unlock (&mu);
171
172With conditional critical sections, the equivalent is:
173	/* variable declarations */
174	nsync_mu mu = NSYNC_MU_INIT;  /* protects i */
175	int i = 1;
176
177	/* Condition */
178	int int_is_zero (void *v) {
179		return (*(int *)v == 0);
180	}
181
182	...
183
184	/* Waiter */
185	nsync_mu_lock (&mu);
186	nsync_mu_wait (&mu, &int_is_zero, &i)
187	/* i is zero ... */
188	nsync_mu_unlock (&mu);
189
190	...
191
192	/* Decrementer */
193	nsync_mu_lock (&mu)
194	i--;
195	nsync_mu_unlock (&mu);
196
197For the cost of writing a function that evaluates the desired
198condition, the waiter's while-loop, and the decrementer's
199signalling are handled by the implementation.
200
201In most cases, this makes code easier to write and debug.
202
203The primary cost is that the implementation must check whether any waiters'
204conditions have become true when releasing the lock.  This cost becomes most
205noticable when threads wait on many distinct, false conditions.  In such cases,
206some or all of the conditions can be converted to use condition variables and
207explicit signalling.
208
209C++ users may be tempted to wrap this functionality in a way that uses
210lambda expressions for the conditions.  This will work, but may be less
211efficient, because C++ does not provide a means to detect whether two lambda
212expressions evaluate the same function.  This may force the implementation to
213evaluate the same false condition many more times than it otherwise might.
214
215Reader/writer locks
216-------------------
217There is no particular reason why a reader/writer lock need be
218significantly slower than a simple mutex.  In both cases, the lock can be
219acquired or released with a single atomic read-modify-write sequence.
220Thus, the type nsync_mu is a reader/writer lock.  Locks with reader-sections
221can be used with condition variables and conditional critical sections
222without affecting correctness.
223
224Cancellation
225------------
226The pthread API allows the cancellation of individual threads,
227and once a thread has been cancelled, it is expected to terminate soon.  This
228can work well in some cases, but may not be convenient if an activity is
229associated with many threads, or if threads routinely act on behalf of multiple
230activities.
231
232In nsync, cancellation involves an object separate from the thread, called an
233nsync_note.  An nsync_note is conceptually a boolean that makes a single
234transition from false to true: it starts off "unnotified", can be notified:
235- by an explicit nsync_note_notify() call,
236- due to a timeout, or
237- due to the transition of an optional parent nsync_note.
238So, for example, in a network server, a request with a deadline
239might have an nsync_note associated with it.  Activities associated with
240that request might each have a child nsync_note, possibly with shorter
241deadlines.   A cancellation request from the original caller
242might cancel the parent, which would cancel all the children.
243
244The calls nsync_cv_wait_with_deadline() and nsync_mu_wait_with_deadline() take
245both a deadline and a pointer to an nsync_note, and will wake when the awaited
246condition becomes true, when the deadline (if any) expires, or when the
247nsync_note becomes notified.  The return value indicates which of these
248occurred.
249