docs.h
Semantically the two methods are equivalent, the ParComm method is just easier to use, faster and less memory hungry. It can also be re-used in a looping process without re-allocating memory every loop.
This was considered at some point but normally a barrier and bucket have unit-time behaviour for all syncing/flushing because they build a queue in the same format as the run queue - so putting the processes back on the run queue is a matter of 2 pointer assignments, even for a barrier of size 1 million (or more!). Allowing ParCommItem for Barrier and Bucket objects would involve changing this behaviour to linear-time, i.e. O(n), for the final sync of Barrier and for flushing, so it was decided against.
Theoretically that is potentially true but it is a bad idea for several reasons:
- It is not always clear (or guaranteed) when context switches will occur. A hidden context switch could ruin code that relies on not having one
While I would never rule out bugs in the library it is more likely to be a case of misuse of the library. Most commonly, processes that are in different threads using non-thread-safe objects (such as One2OneChannel) or multiple processes using a non-shared channel end, or processes syncing on a barrier they haven't enrolled on. There are probably more ways to make the library crash too. This is intended to be the case -- adding run-time checks for these problems would impact on performance, and as Stroustrup pointed out, you can always build a safe slow interface on top of a fast dangerous one but never the other way round...
Deadlock occurs when there are no processes left on the run queue in a thread (and none waiting on timeouts). This should never occur under normal operation, and is definitely a condition that should cause termination. It is usually caused by processes involved in a ring inputting from each other in a circular fashion such that none of the inputs will complete, or processes syncing on a barrier except one that is deadlocked in some other way, or processes inputting from channels that have no outputter at the other end
The function calls Parallel, Sequential, spawnProcess, spawnAsNewThread etc all have the same expectation of a process pointer passed to them -- it is a process that has been declared on the heap. This is because the library calls delete on the object once it has finished running. If you wish to return some data once the process has finished running, use channels (rather than trying to store the data in the process class)
The boost libraries are a collection of brilliant C++ classes. I view them as an expansion to the standard library, filling in holes and providing new things. Boost is a dependency of the C++CSP library. A lot of people find it hard to install boost because it has a complicated build procedure. But: at the time of writing, C++CSP does not the compiled boost library; only the boost headers are needed. This means you don't need to build the boost library. Just download the distribution and copy the boost/ directory of headers contained within to somewhere in your include path. Although on gentoo at least, you can just do "emerge boost", and that works fine.
As mentioned above, C++CSP only requires the boost header files, not the compiled library. This means that the boost headers are needed at compile-time (presumably on a cross-compiling system), but after it is compiled you don't need to worry about boost.
C++CSP aims to be as portable as it can be but it is possible that either your configuration or your platform has not been tried before and has thrown up an unforeseen problem. Please contact the maintainer with details of the problem, who will be happy to try and rectify the problem or include support for your platform in the library.
Here is a copy and paste from an e-mail that seems to roughly cover the point:
I couldn't find a satisfactory solution to the blocking system calls problem. For simple problems it is possible to just accept the blocking of your thread, but for real concurrent programs - surely what CSP is great for - that's not acceptable. In most programs you inevitably end up having one CSP process that wants to hear from two different blocking system calls and ALT over them, as you want to. Clearly you need to farm out threads/processes to make the call and report back. In the old threads method (now removed), you'd use channels built on semaphores. But to ALT over two semaphore-channels you can't wait() on either one; you must poll the semaphores alternately. Polling semaphores (i.e. not blocking on them) turns out to carry a surprisingly large overhead on both Linux and Windows.
This overhead turned out to be larger than calling select() over two sockets (select() being that rare godsend - an OS call that is the equivalent of the ALT, allowing you to select between many usually blocking socket reads). So even though it seems like the worse idea, I went with having many NET machines (i.e. processes) for proper concurrency rather than multiple threads which were in fact slower. This also allowed me to stop worrying about some of the problems with shared memory between threads (e.g. the process run queue used to have to be in TLS, and added a little bit of overhead for every context switch).
Of course, I wish there was an OS with threads of the right granularity to use as CSP processes (fiber-equivalents being a little too light in that they can't block separately, and threads being too heavy in terms of context switching and inter-thread communication), with good inter-thread communication that supports the ALTing concept. But alas, it seems OS-designers don't have the same views on concurrency as us CSP-lot, though I think it is slowly swinging our way as things like dual-core and multi-processor becomes ever more prevalent. Rant over! ;-)
Ah, pthreads. This is a complex one. The pthreads library is part of the glibc package that comes as standard on all GNU/Linux distributions. It is linked to if any library you link with your program also uses pthreads (such as SDL, among many others). From what I can understand from long trawls through the pthreads code, the situation is thus:
Pthreads needs some way to implement TLS (Thread-Local Storage). So it needs some way to identify between threads when they share the same memory space and execute the same code. The two ways it seems to offer are:
- Store the thread identifier at the bottom of the stack, since each thread must have its own stack.
- Store the thread identifier in a largely unused register (gs on the x86 architecture).
Which option it picks depends on your Linux kernel version. It seems to me that on version 2.3.99 or before it uses the first method, and on version 2.4.00 and onwards it uses the second. However, glibc on certain GNU/Linux distributions seems to not fit with this rule, so I'm not sure any more. The reason the first option conflicts with C++CSP is that C++CSP allocates a new stack for each process, so pthreads can no longer find its identifier at the top of the stack.
If you have this error, the simple solution would be to not compile with pthreads, but I realise this is unavoidable for most purposes. The other option is to maybe upgrade your kernel and recompile glibc. Also not a great option, depending on your situation. If this problem persists for people then it may be time to contact the glibc team for a little help.
The above advice is applicable if you are using the setjmp/longjmp method. If you are using GNU pth as your underlying mechanism, I gather that there are added problems linking GNU pth with pthreads: this mailing listpost shows that there is a conflict involving errno, and this post suggests that the two libraries simply weren't designed to be linked together. These posts are from three years ago however; things may have changed since.
Generated on Wed May 18 22:51:50 2005 for The Kent C++CSP Library by
1.4.2