Thread-safety of the classes

The thread-safety level of the classes depends greatly on the thread-safty of the underlying parser and scanner generators (Yacc and Lex).

If they produce thread-safe code, then the classes are thread-safe.

For example, Bison allows you to specify the parser to be reentrant (does not depend on global variables or static data) via the definition pure_parser. If this is desired for the client application, then the grammar can define that, and the ylmm::basic_parser class will respect that.

That said, it is actually easy to make the client code thread-safe using a little disciplin. If the client code derives classes from ylmm::basic_parser and ylmm::basic_scanner and implment locking in these classes, the client code is for all pratical purposes thread safe. The only caveat, is that all access to the parser and scanner should go through that derived layer - client code must not manipulate the global variable yylval and similar directly. That disciplin is in practise easy to achive, as a C++ programmer will in general be more comfortable manipulating an object of a class rather than some global low-level variable.

That said, there is one point where the multi-threaded programmer must take care. The class ylmm::basic_messenger has a static member ylmm::basic_messenger::_default_handler which is the default message handler. That member is initialised as a singleton, meaning it may potentially fall pray to race conditions.

To protect against that, ylmm::basic_messenger accepts a template argument Lock, which should be the type of a thread syncronisation locking class (a mutex). The class must:

  • Be default constructable
  • Have a member function lock with the semantics of aquiring a mutually exclusive lock. For example on a POSIX platform, that member function could call pthread_mutex_lock, while on a Win32 platform it could call EnterCriticalSection.
  • Have a member function unlock with the semantics of releasing the previously aquired mutually exclusive lock. On a POSIX platform that member function could call pthread_mutex_unlock, while on a Win32 it could call LeaveCriticalSection.

All classes that manipulates, directly or indirectly, a ylmm::basic_messenger has a similar argument, which is passed on to the ylmm::basic_messenger class. So, for a multi-threaded application on a platform that provides a POSIX interface, this could spell out to be

    #ifndef _PTHREAD_H
    #include <pthread.h>
    #endif

    class thread_lock { 
    private: 
      pthread_mutex_t _mutex;
    public:
      thread_lock() { pthread_mutex_init(&_mutex); }
      ~thread_lock() { pthread_mutex_destroy(&_mutex); }
      void lock() pthread_mutex_lock(&_mutex); }
      void unlock() pthread_mutex_lock(&_mutex); }
    }; 

    class thread_guard {
    private: 
      thread_lock& _lock;
    public:
      thread_guard(thread_lock& l) : _lock(l) { _lock.lock(); }
      ~thread_guard() { _lock.unlock(); }
    };

The client can then use that class as an argument to the parser and scanner classes:

    class node;
    
    class scanner : public ylmm::basic_scanner<node,ylmm::location,
                                               0, thread_lock>;
    class parser : public ylmm::basic_parser<node,ylmm::location,
                                             0, thread_lock>;

Both ylmm::basic_parser and ylmm::basic_scanner defines the nested type lock_type, which the application can then use as applicable. For example to protect the token object in the parser, the application could do:

    class parser : public ylmm::basic_parser<node,ylmm::location,
                                             0, thread_lock> { 
    private:
      lock_type _lock;
      ylmm::basic_parser<node,ylmm::location,0, thread_lock> _scanner;
    public: 
      int scan(void* arg) { 
        thread_guard g(_lock);
        return _scanner->next(&token());
      }
      ...
    }
(Another C++ idom was used here - the guard that exploits the scoping of variables - sometimes it pays off reading the periodicals).
Top of page
Christian Holm (home page)
Last update Fri Jul 8 12:58:03 2005
Created by DoxyGen 1.4.3-20050530