-
Notifications
You must be signed in to change notification settings - Fork 0
Included Libraries
There are currently four C++ libraries, COM, GUI, OBJ, and PUB.
Include files are found in: ~/project/src/cpp/inc/LIB/.
Source files in: ~/project/src/cpp/lib/LIB/.
, and their subdirectories.
Library test source files in: ~/project/src/cpp/LIB/Test/.
Libraries are built using:
cd ~/project/obj/cpp/lib
make
## This creates all four libraries
or
cd ~/project/obj/cpp/dll
make
## This creates DLL or .so libraries
Library tests are built using:
cd ~/src/cpp/obj/lib/LIB/Test
make all
In all the above, replace LIB with the lower case library name: com, gui, obj, or pub.
All libraries use thread-safe implementations where expected, such as in Thread.h, Mutex.h, Dispatch.h, and Latch.h. Certain other objects provide some additional thread-safe functionality. For example, Debug objects automatically use and expose a static recursive latch to serialize output operations. This is handy in multi-threaded environments so that outputs from different threads aren't intermingled.
Debug.h along with Logger.h define utility debugging routines. There is similar functionality in the PUB library, but external static functions are called there using the "pub::debugging" namespace qualifier. In PUB, logging functionality (adding thread id and time stamp) is (an optional) part of the Debug object rather than sub-classed.
Dispatch.h provides a simplified multi-threading environment. There are three basic Dispatch objects: DONE, ITEM, and TASK. The DONE object is a callback object, called when an ITEM has nothing left to process. The ITEM is a work item, describing what work needs to be done. It also contains a pointer to its associated DONE object. The TASK is a work item processor. A given TASK only processes one work ITEM at a time. Any number of threads can simultaneously add an ITEM to a TASK's work queue using the TASK->enqueue(ITEM*) method. A TASK may either complete work for an ITEM, or pass it along to another TASK. Work for an ITEM completes when ITEM->post() is invoked.
Note: The PUB version of Dispatch.h can process nearly twice as many TASK->enqueue() functions per second as the COM version.
Latch.h contains spin-lock latch implementations. The PUB version of Latch.h contains objects suitable for use with std::lock_guard. Spin lock latches are usually used for extremely short latch lock durations.
The PUB LinkedList.h is essentially the same as the COM List.h, with the addition of an atomic list extender which significantly reduces the overhead of FIFO list operation. Providing this and the Dispatch interfaces to the open source community is one of the main reasons this distribution exists now rather than later. It's really spiffy. That said, if anyone should be really interested in using it, I'd like to build a better stress tester before that happens. Open an issue.
The COM library was created first. The intent was to create a library that would be completely source compatible between operating systems. This didn't work out as well as hoped. For example, the Socket class created its own message and socket option values (largely the same as Fedora Linux) and automatically handled some common error conditions. While this worked, it was often necessary to look at the library source code to see exactly how some errors were handled. The Thread class was more successful, hiding differences between Windows and Linux implementations. This library does not use a namespace to avoid name collisions.
Atomic.h was possibly implemented before the Gnu compare_exchange built-ins and certainly before I knew about them. While this is now obsolete, some may find it interesting from a historical perspective.
The GUI library was created next. The intent was to provide a source compatible graphics library. This also didn't work out as well as hoped. The library, such as it is, does work even on Windows. I was never happy with the result though and have come to the conclusion that for my purposes it's probably easier to use some other package written by someone a lot more familiar with graphic output than I am.
During and after building the GUI library, the OBJ (object) library was created. This was and is an attempt to create a garbage-collected C++ environment. After working on this for a while, I found out about std::shared_ptr and figured it was better to use that than re-invent the wheel. I'm not 100% certain that this is true because the environment I created was completely thread-safe. This may not be true with std::shared_ptr since std::atomic_shared_ptr won't be widely available until C++20.
In testing the OBJ library, the biggest problem I found was with cascaded deletes. You can't just recursively handle them because it's easy to run out of stack space. If you create a list of delete to-do operations, it's easy to starve one thread which winds up doing the delete work for other threads. I got the best performance running a separate thread whose only job is to process deletes.
While I wonder how std::shared_ptr handles cascaded deletes and whether it even properly completely handles all the multi-threading problems that can arise, I haven't wondered hard enough to bother rewriting my OBJ stress test to find out. Boost has a std::atomic_shared_ptr and I expect that they know about all the problems I know about and then some.
The PUB (public) library is the latest addition to my library suite. In it, the Debug and Dispatch objects have been updated. These versions should be used rather than the COM versions, for both bug fix and performance reasons.
Thread.h basically provides an object wrapper for std::thread. Joinable Threads are mapped, so a thread_id can be used to locate the Thread object.
Worker.h provides a relatively easy to use mechanism for thread re-use while simultaneously (at least somewhat) hiding the existence of threads. There is no limit to the number of Worker threads that can simultaneously exist, but there is a limit to the number of threads that can be re-used. Tested code exists that can dynamically change this limit but, since I found no real use for this functionality, it's not exposed in the interface and versioned out in the implementation. Worker objects are one of the two Dispatch object performance boosters. The other is an improved work item dequeue mechanism.