MSDK 2.11
Copyright 2004-2006, Zlatko Michailov
License
Support
How to Use
Platform Requirements
Installation
Compilation
Building Samples
Content
What's New In MSDK 2.11
What's New In MSDK 2.10
What's New In MSDK 2.01
Primitives
Mutex
Event
Blocking Counter
Semi-Mutex
Thread Support
Message
MessagePayload
Blocking Sequence
Thread
Buffer
Utilities
Process Support
Log
LogMessagePayload
Config
Service
Multithreading Applications
Single Writer - Multiple Readers
Thread Pool
Samples
readwrite
async-threadpool
async-bqueue
log
config
service
License
MSDK contains original C++ source code and documentation written by Zlatko Michailov.
All copyrights belong to Zlatko Michailov. MSDK is distributed under the
BSD License.
Support
MSDK is an Open Source project. To receive or provide support, please visit the
MSDK Project Home.
How to Use
Platform Requirements
MSDK supports the 32-bit Windows platform at two levels:
Win32 API and Microsoft Visual C++ Run-Time library (MSVCRT).
Some process-related feature require CRT.
UNIX platforms are supported at the level of the Pthread library.
Installation
Extract the contents of the archive to a directory of your choice.
No files are copied outside the target directory. There are no side
effects to the operating system. Multiple version of MSDK can co-exist on the
same machine. To uninstall MSDK, delete the installation directory. MSDK is
only needed for compiling your applications. MSDK is not needed at execution time.
Compilation
MSDK chooses an underlying library based on the existence of
the following preprocessor definitions:
- WIN32 32-bit Windows operating system, Win32 API
- WIN32 and _MT - 32-bit Windows operating system,
Microsoft Visual C++ Run-Time library (MSVCRT)
- Otherwise - UNIX operating system; Pthread library
To use the MSDK, include the root header file, msdk.h,
from the installation directory, e.g. if MSDK is installed in directory ../msdk:
#include "../msdk/msdk.h"
msdk.h includes the rest of MSDK.
On UNIX platforms you have to add the following libraries to
link against: /usr/lib/libstdc++.so.5 and /usr/lib/libpthread.so.
Versions might be different.
Building Samples
There are two make files one for Windows and one for UNIX.
To build the samples, navigate to the samples subdirectory
and execute the following command:
WIN32
nmake -f makefile-win
UNIX
make -f makefile-unix
Content
All MSDK classes belong to namespace msdk.
You should either prepend each class name with msdk::
or you should have the following statement:
using namespace msdk;
Starting with MSDK 2.10 a new, nested namespace is added
msdk::process for process-related functionality.
What's New In MSDK 2.11
MSDK 2.11 is a maintenance release over MSDK 2.10. It's purpose is to improve the
existing baseline and to prepare it for the new features that are already under
discussion.
Recently the license was changed was changed to BSD which
is allows much more freedom to the developer than the prevously used LGPL, and GPL.
The following fixes are included in MSDK 2.11:
- Replace the call to PulseEvent() with SetEvent() ResetEvent() in line 166 in include/event.h.
Generally, that replacement is not equivalent. However, in MSDK that is fine because access to an
Event instance is serialized.
- Switch to secure CRT functions while still supporting compilers without secure CRT.
- Add MSDK_SERVICE_MAIN2 stub macro that accepts a custom service name in addition to the service thread.
- Support environment variables for Visual Studio 2003 and 2005. Add samples/vsvars32.bat.
Remove samples/vcvars32.bat.
What's New In MSDK 2.10
MSDK 2.10 adds support for complete multithreaded applications -
logging, configration manipulation, and service skeleton.
The process support sits on top of the core MSDK. Hence, the code
rsides in a nested namespace - msdk::process.
Adding an extra level to MSDK involved more rigorous testing of
the core and some enhancements were made without breaking
backward compatibility.
Enhancements to Exisitng Classes
- Class Message now supports: 1) construction from a MessagePayload
reference, and 2) extraction of paylod. MessagePayload is a new interface
that makes serialization to- and from Message simple.
- Class Thread now accepts an extra, timeout (in seconds), parameter.
That timeout is used by the Stop() method in Windows. If a thread does not
stop in the given timeout, it is terminated. The default is 30 seconds.
It is the application developer who knows best what is the appropreate
timeout for each Thread derivate.
Additions
- A set of utility types and functions was added for common use.
- Class Buffer is a very simple class that given a integer,
allocates space to accomodate a character string of that length.
A Buffer instance is automatically cast to ( char* ) and
( const char* ). This functionality is supported by class
std::string but it showed memory leaks under Linux.
- Class Log is a Thread-derivate that records messages in
text files. The reason why Log is a separate thread is to
prevent the worker threads from wasting time accessing the
file system. Second, the Log thread may impersonate an
account that has access to places of the file system where
worker threads do not have access.
- Class Config manipulates text configuration files. A file
contains configuration rows and comments. A row contains columns.
Different rows do not need to have the same number of columns.
Columns are added dynamically when needed.
- Class Service provides a skeleton for an NT service
under Windows, and a daemon under UNIX. A Thread-derivate class
must be defined to represent the body of the service. An
instance of that thread is created, started, and stopped by the
service stub.
What's New In MSDK 2.01
The new features of MSDK 2.01 target thread initialization. In MSDK 2.00 a ThreadPool
instance could only create Thread instances using the default constructor. Thus
the pool was not able to pass any initialization data before the threads start.
MSDK 2.01 enhances the thread initialization process as follows:
- Class Thread exposes a public initialization method with a default implementation
that saves the passed parameter into an existing data member.
- Successors of class Thread may override the default implementation and provide
their own customized initialization.
- Class ThreadPool has a protected overridable method that is invoked to initialize
each thread right before the thread is started. The default implementation redirects
the call to the thread's public initialization method. That implementation may
be overridden by ThreadPool successors.
Primitives
Mutex
Mutex is a standard synchronization primitive. It may be
acquired (locked) by no more than one thread at a time. Subsequent acquisition
attempts cause the calling thread to be blocked without consuming CPU. When a
mutex is released (unlocked), a blocked thread is chosen randomly to acquire
the mutex. The MSDK implementation of the mutex primitive allows one thread to
acquire a mutex multiple times (recursively). To release the mutex, the thread
is requied to unlock it the same number of times.
Public methods of class msdk::Mutex:
bool Lock() - Attempts to acquire the mutex. Returns
true on success, false on failure.
bool Unlock() - Attempts to release the mutex. Returns
true on success, false on failure.
WIN32
operator HANDLE() - Returns the HANDLE of the Win32
mutex object to be used in other Win32 API calls.
UNIX
operator pthread_mutex_t*() - Returns the
pthread_mutext_t* of the Pthread mutex object to be used in other Pthread API calls.
Event
Event is also a standard synchronization primitive. It may
be waited on or explicitly signaled. After construction an event instance is
non-signaled, i.e. waiting is enabled. The MSDK implementation
supports three types of signaling:
- Pulse- If there are waiting threads, one is chosen randomly and is waken
up. The event instance becomes non-signaled. If there are no threads
waiting, the event instance automatically becomes non-signaled and the
signal is "lost". This is the only signaling type supported natively by
the Pthread library. On the Windows platform this is equivalent to calling
PulseEvent() on an automatic event object.
- Signal (default) - This is the default type of signaling in MSDK. A signal may be
consumed exactly once. If there are waiting threads, one is chosen
randomly and is waken up. The event instance becomes non-signaled. If
there are no waiting threads, the event remains signaled until one thread
attempts to wait on it. That thread is not blocked and the event instance
becomes non-signaled. This is not supported natively by the Pthread
library. On the Windows platform this is equivalent to calling SetEvent()
on an automatic event object.
- Permanent- This allows a signal to be consumed infinite number of times. The
event instance remains signaled until resetting explicitly. All attempts
to wait return immediately while the event instance is signaled. This is
not supported natively by the Pthread library. On the Windows platform
this is equivalent to calling SetEvent() on a manual event object.
Public methods of class msdk::Event:
bool Wait() - Attempts to wait on the event infinitely.
Returns true on success, false on failure.
bool Signal( SignalType signal = SIGNAL) - Attempts to
signal the event. Possible signal types are PULSE, SIGNAL, and PERMANENT.
Returns true on success, false on failure.
bool Reset() - Attempts to reset the event as
non-signaled. Returns true on success, false on failure.
WIN32
operator HANDLE() - Returns the HANDLE of the Win32
event object to be used in other Win32 API calls.
UNIX
operator pthread_cond_t*() - Returns the pthread_cond_t*
of the Pthread event object to be used in other Pthread API calls.
Blocking Counter
Blocking counter is a new synchronization primitive
introduced in MSDK. It allows to incremented and decremented without blocking
as long as the value remains non-negative. This means it could be incremented
at any time without waiting. When a decrement attempt is made, if the value is
positive, the counter is decremented. If the value is 0, the calling thread is
blocked until some other thread increments the counter. This implementation of
the blocking counter supports only incrementing/decrementing by 1, and a
blocking counter is always initialized to 0. A blocking counter instance
signals two events: Clear, when the counter value becomes 0; and Dirty, when
the counter value changes from 0 to non-zero.
The blocking counter primitive becomes very useful when it
is embedded in more complex objects. For instance, a blocking queue (see Thread
Support) allows pushing at any time and popping only when there are items in
the queue. Pushing maps to incrementing a blocking counter, and popping maps to
decrementing. This makes the blocking queue implementation straightforward.
Public methods of class msdk::BlockingCounter:
int Value() - Returns the value of the counter.
bool IsClear() - Returns true if the value of the
counter is 0 (clear), false otherwise.
bool WaitUntilClear() - Blocks the calling thread until
the value of the counter is clear. If the value is already clear, the method
returns immediately. Returns true on successful wait, false on wait failure.
bool WaitUntilDirty() - Blocks the calling thread until
the value of the counter is dirty (non-zero). If the value is already dirty,
the method returns immediately. Returns true on successful wait, false on
wait failure. Both Clear and Dirty event are signaled permanently. This means
hat more than one thread may be released at the same time. If exclusive access
is needed, special care should be taken like in the implementation of the
BlockingDecrement() method.
bool Increment() - Increments the value of the counter
by 1 without blocking. Returns true on success, false on failure.
bool BlockingDecrement() - Decrements the value of the
counter. If the value is dirty, does not block. If the value is clear, blocks
the calling thread until the value becomes dirty. Returns true on success,
false on failure.
BlockingCounter& operator++() - Shortcut to Increment().
BlockingCounter& operator--() - Shortcut to BlockingDecrement().
Semi-Mutex
Semi-Mutex is another synchronization primitive introduced
in MSDK. It supports the same exclusive locking functionality as the standard
mutex, plus an extra pair of methods for shared locking. Unlimited number of
threads are allowed to hold a shared lock while no exclusive lock is held on
the same semi-mutex instance. When a thread attempts to lock a semi-mutex
exclusively, all further shared lock attempts are blocked until the thread with
the exclusive lock releases the lock. The thread that attempts to lock
exclusively waits for all threads that are currently holding shared locks to
release their locks. A semi-mutex makes the implementation of a common
single-writer-multiple-readers workflow straightforward.
Public methods of class msdk::SemiMutex:
bool ReadLock() - Obtains a shared lock. If there is a
thread holding an exclusive lock, the calling thread is blocked until the
exclusive lock is released. Otherwise the method returns without blocking.
Subsequent attempts to obtain an exclusive lock will be blocked until all
shared locks are released. Returns true on success, false on failure.
bool ReadUnlock() - Releases a shared lock. Does not
block. Returns true on success, false on failure.
bool Lock() - Obtains an exclusive lock. If there are
currently shared locks or another exclusive lock, the calling thread is blocked
until all locks are released. All subsequent attempts to obtain any lock are
blocked until the calling thread releases its exclusive lock. Returns true on
success, false on failure.
bool Unlock() - Releases an exclusive lock. Returns true
on success, false on failure.
Thread Support
Both Win32 API and the Pthread library expose sufficient
functionalities for thread support. However, they follow different paradigms
and neither of them is wrapped by C++ classes. The MSDK thread support
classes unify the essential functionality through platform-independent C++ classes.
Message
Message is a thread-support primitive that carries a command
and a data buffer. In Windows messages may carry a command and up to two 32-bit
integer parameters. In UNIX such a feature doesnt exist at all. Class
msdk::Message provides convenient constructors optimized for performance. There
are three options to construct a Message instance from a data buffer:
- DISPOSABLE_AS_IS the supplied buffer is disposable, i.e. dynamically allocated from the
heap, and is not needed any more by the calling thread. This allows the
constructor to keep only a pointer to the buffer as opposed to making a
local copy of the data in the buffer. The destructor will release the
buffer. This option is useful for asynchronous request processing: the
calling thread allocates a buffer and sends it for processing; only the
processing thread can release the buffer when the message is processed.
This option trails fast construction and slow destruction.
- MAKE_DISPOSABLE - the supplied buffer is not disposable. The message must make its own
copy. The message destructor will release that local copy. This option is
also useful for asynchronous request processing when the original data
comes from a static buffer (or by some reason the original buffer is
needed by the calling thread). This option trails slow construction and
slow destruction.
- KEEP_STATIC the supplied buffer is not disposable; it may be used by the message but
it must not be released. This is used when the calling thread wants to be
in complete control of memory management, or when there is no buffer
supplied. This option trails fast construction and fast destruction. This
is the default option unless something else is specified explicitly.
Public members of class msdk::Message:
Message( int command = 0, DataCopy dataCopy = KEEP_STATIC, int
dataLength = 0, void* dataBuffer = 0 ) - Constructor. The dataCopy option is explained above.
Message( const MessagePayload& payload, int command = USER )
- Constructor from MessagePayload.
void Clear() - Clears the content of the message instance.
bool GetPayload( MessagePayload& payload ) const - Clears
Re-initializes paylod from DataBuffer, i.e invokes
payload.FromBuffer( DataLength, DataBuffer ).
int Command - The message command. User commands must be
higher than the value of msdk::Message::USER. There is one system command predefined
msdk::Message::QUIT. The latter is used to attempt to stop a thread gracefully in Windows.
See the explanation under Thread.
bool IsDisposable - True if the buffer is disposable and thus will
be released by the destructor; false otherwise - the buffer is owned by the calling thread.
int DataLength - Number of bytes in the data buffer.
char* DataBuffer - Pointer to the beginning of the data buffer.
MessagePayload
MessagePayload is a pure-abstract class (interface). It provides a standard way
of serializing any class to and from a binary buffer.
Public overridable methods of interface msdk::MessagePayload:
virtual int ComputeBufferSize() const = 0
- Must return the number of bytes needed to store the implementing instance
in a binary buffer.
virtual bool ToBuffer( char* dataBuffer ) const = 0
- Must serialize the implementing instance into dataBuffer. ComputeBufferSize()
has already been called and dataBuffer is already allocated.
virtual bool FromBuffer( int dataLength, const char* dataBuffer ) = 0
- Must re-construct the implementing instance from dataBuffer. dataLength must be
at least the required value and may exceed it (there may have been some extra bytes allocated).
BlockingSequence
Blocking sequence is a thread-support primitive that implements the thread message queue.
Class msdk::BlockingSequence is more generic than a specific message queue in two ways:
- It is a template and may accommodate any payload class.
- It implements queue- as well as stack functionality.
The MSDK blocking sequence exceeds the Win32 thread message
queue functionality by allowing multithreading at both ends of the queue. For
instance, in Windows any thread can post a message to another thread but only
the owner thread may pop the messages. Thus a Win32 message queue supports
multithreading only at the pushing end.
Public methods of class msdk::BlockingSequence< class PAYLOAD >:
bool IsEmpty() - True if the sequence is empty; false otherwise.
bool Pop( PAYLOAD& payload ) - Loads the front
payload into the provided reference and removes it from the sequence.
bool Peek( PAYLOAD& payload ) - Loads the front
payload into the provided reference without removing it from the sequence.
bool PushBack( const PAYLOAD& payload ) - Puts a
copy of the payload at the back of the sequence queue behavior.
bool Push( const PAYLOAD& payload ) - Puts a copy of
the payload at the front of the sequence stack behavior.
Thread
Thread is a well-known OS primitive. Historically threads are first introduced in
VMS and later made popular in its spiritual successor Windows. Thus Windows has a
longer history than UNIX of employing threads. The Win32 API and the Pthread library
follow different paradigms, which makes developing cross-platform code tedious.
The MSDK Thread primitive exposes a common API that tries to unify the two paradigms.
Class msdk::Thread is the most essential class of the thread
support in MSDK. Creating an instance of class msdk::Thread does not spawn a
new OS thread. To do so, the Start() method must be explicitly invoked.
Stopping a thread is slightly different in Windows and UNIX. Windows does not
have a mechanism to stop a thread gracefully in the general case a thread may
be terminated (brutally killed), or it may be posted a WM_QUIT message, in
which case the thread must be parsing its own message queue for that to work.
In UNIX the Pthread library maintains a cleanup stack for each thread, so that
when it is canceled, the cleanup procedures a popped from the stack and
executed. With regard to thread execution Windows outperforms UNIX by providing
each thread with a built-in blocking message queue, while still supporting the
notion of the worker thread.
The thread paradigm of MSDK is closer to Windows in a sense that each thread has
the choice of executing a given task (worker thread) or parse messages from its
own blocking message queue. On top of that MSDK provides the Pthread benefits of
maintaining a startup- and shutdown event handlers.
Public methods of class msdk::Thread:
Thread( void* data = 0 ) - Constructor. Initializes a
new instance with the provided data buffer. Does not spawn a new OS thread.
bool Start() - Spawns a new OS thread. This will cause
the following overridable methods to be invoked on behalf of the new thread in
the given order: Startup(), Run(), Shutdown(). Returns true if a new OS thread
was spawned, false otherwise.
bool Stop() - Attempts to stop a thread gracefully. See
considerations above. This may not be possible for worker threads in Windows.
If the thread responds to this event, the Shutdown() overridable method will be
invoked on its behalf. Returns true if the lower-level call succeeded. It does
not mean the thread was really stopped.
bool Terminate() - Brutally kills the thread. No
overridable method is invoked. This may leave unreleased resources. This method
should be used as a last resort. Returns true if the lower-level call succeeded.
If the thread is still running, there isnt much that could be done.
bool PostMessage( const Message& message ) - Posts a
message to this Thread instance. Returns true if the message was posted
successfully. It doesnt guarantee the thread will process it.
bool WaitUntilFinished() - Blocks the calling thread
until this Thread instance stops. Returns true on success, false otherwise.
void Sleep( unsigned int seconds, unsigned int milliseconds = 0 )
- in UNIX it is not known whether the system sleep() function is cancelable.
This method is guaranteed to be cancelable. Also, this method provides the
convenience to use smaller numbers with high resolution. The time to sleep is:
seconds + milliseconds. Milliseconds must be less than 1000.
ThreadID GetThreadID() - Returns the thread ID that
could be used as a parameter to lower-level function calls.
bool IsThreadRunning() - Returns true when the thread is
known to be running. It may be blocked on shared resources though.
bool IsThreadIdle() - Returns true when the thread is
waiting for new messages.
Public overridable methods of class msdk::Thread:
virtual bool Init( void* initData = 0 ) - May be invoked
before Start() to trigger thread initialization. The method may be overridden by
Thread successors to customize their own initialization. Returns true if
initialization passed, false otherwise.
Protected overridable methods of class msdk::Thread:
virtual void Startup() - This is the first method
invoked on behalf of the OS thread. The default implementation is dummy.
virtual void Shutdown() - This is the last method
invoked on behalf of the OS thread. The default implementation is dummy.
virtual void Run() - This is the main thread procedure.
It is invoked on behalf of the OS thread right after Startup(). The default
implementation consists of a message pump that quits execution when
msdk::Message::QUIT command is sent. Otherwise it invokes ProcessMessage() for
every other message.
virtual void ProcessMessage( const Message& /* message */ )
- Invoked by Run() for every new message in the threads message queue.
In conclusion, to implement a worker thread override Run() with the code of your need.
To implement a message processing thread, override ProcessMessage(). Startup() and Shutdown()
are useful when there are resources to be initialized/released.
Buffer
Buffer is a very simple class that given a integer, allocates space on the heap to
accomodate a character string of that length. A Buffer instance is automatically cast
to ( char* ) and ( const char* ). This functionality is supported by class
std::string but it showed memory leaks under Linux.
Public methods of class msdk::Buffer:
Buffer( size_t length = 0 ) - Constructor.
Buffer( const Buffer& buff ) - Copy constructor.
void Free() - Releases any allocated memory and clears the buffer.
bool Alloc( size_t length ) - Re-allocates memory. If tere is
already some memory allocated, it is released first. Allocates length + 1 bytes to ensure
the trailing '\0' could be accommodated.
size_t Length() const - Returns the current length of the buffer
excluding the byte for the trailing '\0'.
operator char*() - Implicit cast to
( char* ).
operator const char*() const - Implicit cast to
( const char* ).
Utilities
The utilities module is a set of public common-use types and functions:
Types:
ProcessID - Process ID compatible with the type used by
the platform API - DWORD in Windows,
pud_t in UNIX.
Time - Currently this is a synonim of -
tm which is supported in both Windows and UNIX.
Functions:
inline ProcessID CurrentProcessID() - Returns the current process ID.
inline ThreadID CurrentThreadID() - Returns the current thread ID.
Type ThreadID is defined along with class
msdk::Thread.
inline Time CurrentTime() - Returns the current time.
inline int stroptcmp( const char* s1, const char* s2, bool caseSensitive )
- Compares two string with an option of case-sensitivity. The result is the same as the result
of function strcmp(). Function strcasecmp()
is defined in Windows as a mapping to _stricmp().
inline char* strnset( char* s, char c, size_t n, bool force = false )
- Sets up to n chars to ch at position s. Allows using force to go beyon the trailing '\0'.
inline char* strdupcat( const char* s1, const char* s2 = 0 )
- Creates a copy of the concatenation of the two strings. The returned copy must be
freed by the caller.
inline int strchrchr( char* str, char x, char y, int n = -1 )
- Replaces up to n occurrances of a char within a buffer with another char.
The buffer must be writable.
inline int mkdirhier( const char* path, int mode = 0775 )
- Makes sure the entire path exists. May create more than one level of directories.
The path may have a trailing '/'.
Process Support
The process support entities are defined in namespace msdk::process.
Log
Log is a Thread-derivate that records messages in text files. The reason why Log is a
separate thread is to prevent the worker threads from wasting time accessing the file system.
Second, the Log thread may impersonate an account that has access to places of the file system
where worker threads do not have access. Log is a template class. Its parameter is a
MessagePayload-derivate.
Class msdk::process::Log has one public enumerator - LogMessageCommand
for message commands recognized by the default implementation of class Log:
- INIT_LOG - Initialize log. Usually called once before starting logging messages.
The Log instance makes sure the path to the log file exists.
- LOG_MESSAGE - Log an individual message.
- USER_LOG_COMMAND - Other user command that should be nadled by an overriding
implementation.
Public methods of class msdk::process::Log:
Log( int cutOffLevel = LOG_LEVEL_DEBUG ) - Constructor. The cutOffLevel
is a simple way to build instrumented and release versions of the same system. Each LOG_MESSAGE
request has a level. Messages with levels under the cutOffLevel of the Log instance are not logged.
The possible log levels are:
- LOG_LEVEL_DEBUG - Debugging messages that should be excluded from the final release.
- LOG_LEVEL_VERBOSE - Extra information.
- LOG_LEVEL_INFO - Useful information.
- LOG_LEVEL_WARNING - The system encountered some problems.
- LOG_LEVEL_ERROR - The system encountered an error. The operation failed but the
system continues working.
- LOG_LEVEL_CRITICAL - The system cannot continue.
bool Write( const MESSAGE_PAYLOAD& payload, int command = LOG_MESSAGE )
- Wraps up payload into a Message and posts it to the Log. Returns before the message is processed.
Protected overridable methods of class msdk::process::Log:
virtual bool GetLogFileDir( const MESSAGE_PAYLOAD& /* payload */, Buffer& dir )
- Must return the (full) path to the directory where the log file will be placed. The Log will make
sure that directory exists. The payload is used in case on Log instance supports multiple log files.
The payload should give enough information which log file is the subject. dir is already allocated.
virtual bool GetLogFilePath( const MESSAGE_PAYLOAD& /* payload */, Buffer& path )
- Must return the (full) path to the log file. path is already allocated.
void ProcessMessage( const Message& message )
- Overrides msdk::Thread.
Config
Class Config manipulates text configuration files. A file contains configuration rows and comments.
A row contains columns. Different rows may have different number of columns. Columns are added
dynamically when needed. The default column separator is '|' but a different character could be
specified. The default comment character is '#' and comments out the line to its end. It may also
be overriden.
Public methods of class msdk::process::Config:
Config( const char* filePath, char columnChar = ConfigColumnChar,
char commentChar = ConfigCommentChar ) - Constructor.
bool DeleteRow( const char* id, bool caseSensitive = true )
- Deletes a row by row ID.
bool UpdateRow( const char* row, bool caseSensitive = true )
- Updates a row.
bool SetRow( const char* id, const char* row, bool caseSensitive = true )
- A generic methof that implements both modification and deletion of a row. If row is NULL, the row
is deleted. Otherwise row provides the new value for the row.
bool AddRow( const char* row )
- Adds a new row.
bool GenerateRowID( Buffer& id )
- Generates a new textual row ID. id must be already allocated.
bool GetRow( Buffer& row, const char* id, bool caseSensitive = true )
- Finds a row by row ID. The row IDs generated by this implementation contain digits only.
However MSDK allows config files and row IDs to be modified manually.
bool FilterRow( Buffer& row, int n = 0, const ConfigConstraint filter[] = 0,
bool caseSensitive = true ) - Finds the first row that satisfies the constraint.
ConfigConstraint is a struct that has column index (Column) and textual value (Value).
filter is an AND of all the given constraints.
bool FilterRowSeq( long& pos, Buffer& row, int n = 0, const ConfigConstraint filter[] = 0,
bool caseSensitive = true ) - Sequentially finds all rows that satisfy the filter. This method
shuld not be used in conjunction with methods that modify the content of the config file because
that may invalidate pos.
bool MatchFilter( const char* row, int n, const ConfigConstraint filter[],
bool caseSensitive = true ) - Returns true if the row satisfies the given filter and false otherwise.
bool GetRowID( const char* row, Buffer& id ) - Extracts the rows ID (column 0) of the
given row into an already allocated Buffer.
bool GetCol( const char* row, int i, Buffer& col ) - Extracts the value of column i of the
given row into an already allocated Buffer.
bool SetCol( Buffer& row, int i, const char* col ) - Sets the value of column i of the
given row.
bool FindCol( const char* row, int i, const char*& start, const char*& stop, size_t& missing )
- This is a lower-level method for extracting a column from a row. If the column is found: start points to the first
char of the column, stop points right behind the last char of the column and the method returns true.
If the column does not exist: start and stop are undefined, missing returns the number of columns that
must be added before the desired one, and the method returns false.
Service
Class Service provides a skeleton for an NT service under Windows, and a daemon under UNIX. A Thread-derivate class
must be defined to represent the body of the service. An instance of that thread is created, started, and stopped by
the service stub. A Service instance should not be created directly. Instead a macro should be used -
MSDK_SERVICE_MAIN( ServiceThreadClass ) where ServiceThreadClass is that thread-derivate
that implements the body of the service. That macro generates main()/WinMain(). Advanced MSDK users may choose
not to use the macro and write their own main()/WinMain() instead.
Public methods of class msdk::process::Service:
Service( Thread& thread, const char* serviceName = "Anonymous MSDK Service" )
- Constructor.
void Main( bool runAsService = true )
- Main entry point. The parameter runAsService is irrelevant in UNIX since processes are always
on their own. In Windows though, this parameter tells the service whether it should be submitted
to the SCM (true) or whether it should run independently as a normal process (false).
void Exit() - Stops the service body thread and exits from the process.
Multithreading Applications
Single-Writer-Multiple-Readers
Single-WriterMultiple-Readers is a typical scenario in a
multithreading environment some threads may perform their actions while
sharing the resource with other threads (readers), and some other threads require
exclusive access to perform their action (writers). Class msdk::ReadWrite is an
elegant solution based on the MSDK semi-mutex primitive.
Public methods of class msdk::ReadWrite:
bool SafeRead( void* data ) - Acquires a shared lock,
invokes the overridable Read(), and releases the lock.
bool SafeWrite( void* data ) - Acquires an exclusive
lock, invokes the overridable Write(), and releases the lock.
Overridable methods of class msdk::ReadWrite:
virtual bool Read( void* /* data */ ) - To be overridden
with the custom read operation. The default implementation is dummy.
virtual bool Write( void* /* data */ ) - To be
overridden with the custom write operation. The default implementation is dummy.
Thread Pool
Thread pool is another typical scenario in a multithreading environment when scalability
in processing incoming requests is required. The thread pool dispatches each incoming request
to an available thread. (MSDK also provides another way to implement this scenario without
using a ThreadPool instance. See the samples bellow.) Class msdk::ThreadPool is a template that
receives as a parameter a class, PROCESSORTHREAD, that derives from msdk::Thread. That class
must override the ProcessMessage() method. That is where the actual request processing is done.
Public methods of class msdk::ThreadPool< class PROCESSORTHREAD >:
bool Start( int threadCount = 10, bool allMustStart = false )
Attempts to spawn the given number of threads. Returns true if it was
required for all the threads to start and they did, or at leas one thread
started and it wasnt required for all the threads to start, and false otherwise.
int GetRunningThreadCount() - Returns the number of
successfully started threads.
bool Stop() - Gracefully stops all threads and waits for them to finish.
bool PostRequest( const Message& message ) - Dispatches an incoming request.
This will trigger the ProcessMessage() overridable method of one PROCESSORTHREAD instance.
Protected overridable methods of class msdk::ThreadPool< class PROCESSORTHREAD >:
virtual bool InitThread( int threadIndex, void* initData = 0 )
Invoked within the loop of creating and starting up the threads. There is exactly one
invokation per thread. The thread index starts from 0. The default implementation
redirects the call to Thread::Init() and propagates the result. May be overridden by
ThreadPool successors to customize the initialization of their own threads.
Samples
Each sample consists of one .cpp file. To build the samples,
navigate to the samples subdirectory and execute the following command:
WIN32
nmake f makefile-win
UNIX
make f makefile-unix
readwrite
This sample demonstrates how to implement a single-writer-multiple-readers scenario using
class msdk::ReadWrite. The executable outputs messages when an operation starts/ends.
The "vvv" notation means "start", and "^^^"
means "end". For clarity write operation messages are indented to right. The number
in each message represents the ID of the thread executing the operation. What you should see
is that write operations are never nested while read operation may be.
MSDK classes directly utilized in this sample: ReadWrite, Thread.
async-threadpool
This sample demonstrates how to implement an asynchronous request processing scenario using
class msdk::ThreadPool. The executable outputs messages when a request is being processed. Each
message contains the sender thread ID, the ordinal request number for that sender, and the
processor thread ID. The former two numbers are included in the request. What you should see
is that messages originate from all senders and are processed by all processors.
MSDK classes directly utilized in this sample: ThreadPool, Thread, Message.
async-bqueue
This sample also demonstrates how to implement an asynchronous request processing scenario
but this time without using class msdk::ThreadPool. Instead, it uses class msdk::BlockingSequence.
The executable produces the same output as async-threadpool. Watch for the same things.
MSDK classes directly utilized in this sample: BlockingSequence, Thread, Message.
log
Shows how to usee the default Config implementation and log messages to the console.
MSDK classes directly utilized in this sample: Log, LogMessagePayload.
config
Shows how to use the Config class at its three levels: I/O-, Row-, and File.
MSDK classes directly utilized in this sample: Config, ConfigRow, ConfigIO, Buffer.
service
Shows how to implement a platform-independednt service. In Windows the service must be
registered first, e.g. with the INSTSRV tool. The service logs a message at startup, shutdown,
and every 2 seconds while running.
MSDK classes directly utilized in this sample: Thread, Log, LogMessagePayload.
Copyright 2004-2006, Zlatko Michailov