[thread|interprocess] Boost.Synchro: Towards a generic view of synchronization mechanisms

Hi, Currently there are two libraries defining mutex, condition variables and locks in Boost (Boost.Thread and Boost.Interprocess), which have similar interfaces but with some differences. This has as consequence that we can not interchange them. One of the reasons is that we are not working with concepts, and algorithms. Both boost::mutex and boost::interprocess_mutex are models of the same Lockable concept, but not because they share a common syntax but because they share a common semantics. Can we see a binary_semaphore as a Lockable? Well, usually a binary_semaphore will define operations such a post and wait, so binary_semaphore do no share the common syntax as boost::mutex and boost::interprocess_mutex. On the semantic side wait can be assimilated to lock and post to unlock. By the way, there are also other syntactical differences between the Boost.Thread and Boost.Interprocess classes. So how can we write gerenic code that can be used with every model of the concepts. The idea consist in switching from the OO paradigm to the functional paradigm. We found the same approach Boost.MPL and Boost.Fusion between others. So instead of requiring that a model of the concept Lockable defines the member functions lock and unlock, we require that the functional expressions lock(m) and unlock(m) are well founded. A generic Synchro library can define a default implementation that forwards to the member function. namespace synchro { template <typename Lockable> void lock(Lockable& lockable) { return lockable.lock(); } } Now we can overload the lock function on a binary_semaphore as namespace synchro { void lock(binary_semaphore& sem) { return sem.wait(); } } Just another example for a class template anotherLockableModel that models the Lockable concept but that with acquire/release functions. We need to overload again the lock function. namespace synchro { template< typename T> void lock( anotherLockableModel<T>& lockable ) { return lockable.acquire(); } } However, there are cases where overloads do not suffice (as described in N1295 - Partial specialization of function templates), and we need to use partial specialization of function templates. On compiler that do not support partial specialization of function templates we can use a workaround, that is, use partial specialization of class templates. The default implementation will now forward to a static function of a class which has forward to the member function namespace synchro { namespace partial_specialization_workaround { template< typename Lockable > struct lock { static void apply( Lockable& lockable ) { return lockable.lock(); } }; } template <typename Lockable> void lock(Lockable& lockable) { return partial_specialization_workaround::lock<Lockable>::apply(lockable); } } So now the user can partially specialize the class template partial_specialization_workaround::lock and define the apply function as needed: namespace synchro { namespace partial_specialization_workaround { template< typename T> struct lock <anotherLockableModel<T> >{ static void apply( anotherLockableModel<T>& lockable ) { return lockable.acquire(); } }; } } Once we have all the Lockable operations defined in this way, and do the same for ConditionVariable, we can define in a generic way unique_lock, shared_lock and upgrade_lock that works with boost::shared_mutex as boost::interprocess::interprocess_upgradable_mutex. This could leverage these libraries to define its own locks classes. Ion, Anthony, Boosters what do you think of applying this approach to get a generic Synchro library? Should the partial_specialization_workaround be applied systematicaly or it complicates things? Thanks, Vicente

vicente.botet wrote:
Hi,
Currently there are two libraries defining mutex, condition variables and locks in Boost (Boost.Thread and Boost.Interprocess), which have similar interfaces but with some differences. This has as consequence that we can not interchange them.
Yes, it's a pity. However, the aim of Boost.Interprocess was to adopt the standard C++ interface. At the time Interprocess was designed there was not such interface and the interface Howard Hinnant proposed long time ago. I will surely, adapt them standard interface and deprecate Boost.Interprocess locks (but maintain them for backwards compatibility) so that all synchronization primitives can be reused, including future lock algorithms. Semaphores are different because they have different uses. If a programmer wants to use a semaphore as a mutex, I think he should write his own adapter, or in the future, write a concept_map. Regards, Ion

----- Original Message ----- From: "Ion Gaztañaga" <igaztanaga@gmail.com> To: <boost@lists.boost.org> Sent: Friday, February 06, 2009 6:52 PM Subject: Re: [boost] [thread|interprocess] Boost.Synchro: Towards a generic view of synchronization mechanisms
vicente.botet wrote:
Hi,
Currently there are two libraries defining mutex, condition variables and locks in Boost (Boost.Thread and Boost.Interprocess), which have similar interfaces but with some differences. This has as consequence that we can not interchange them.
Yes, it's a pity. However, the aim of Boost.Interprocess was to adopt the standard C++ interface. At the time Interprocess was designed there was not such interface and the interface Howard Hinnant proposed long time ago.
I will surely, adapt them standard interface and deprecate Boost.Interprocess locks (but maintain them for backwards compatibility) so that all synchronization primitives can be reused, including future lock algorithms.
This will be great but not completly necesary with the function approach. There is a difference that breaks more that the others, the exception thrown are different in both cases from the standard ones. This force to use a try-catch when the exceptions are not the standard ones. Anyway, could you say when do you think this should be available?
Semaphores are different because they have different uses. If a programmer wants to use a semaphore as a mutex, I think he should write his own adapter, or in the future, write a concept_map.
I think that the function template approach is more open in the same way we have free functions e.g. begin() end() on the range library. The partial specialization of the function templates is a kind of concept_map. Maybe I'm wrong but concept_maps will not be widely available too soon. So if I understand you think that the partial specialization of the function templates approach is not the good approach in this case for the mid_term. Please let me know, your opinon count a lot to me. Thanks, Vicente

vicente.botet wrote:
This will be great but not completly necesary with the function approach. There is a difference that breaks more that the others, the exception thrown are different in both cases from the standard ones. This force to use a try-catch when the exceptions are not the standard ones.
Anyway, could you say when do you think this should be available?
When the standard thread interface is definitely approved my the committee. Now it's in the working paper, but there are pending issues.
Semaphores are different because they have different uses. If a programmer wants to use a semaphore as a mutex, I think he should write his own adapter, or in the future, write a concept_map.
I think that the function template approach is more open in the same way we have free functions e.g. begin() end() on the range library. The partial specialization of the function templates is a kind of concept_map. Maybe I'm wrong but concept_maps will not be widely available too soon.
So if I understand you think that the partial specialization of the function templates approach is not the good approach in this case for the mid_term. Please let me know, your opinon count a lot to me.
I don't know, my feeling is that Boost.Thread and Boost.Interprocess should be interoperable without an additional library, just as a future standard Interprocess C++ should be compatible with existing std::thread.
Thanks, Vicente
Regards, Ion
participants (2)
-
Ion Gaztañaga
-
vicente.botet