13

I have to use a library that makes database calls which are not thread-safe. Also I occasionally have to load larger amounts of data in a background thread.
It is hard to say which library functions actually access the DB, so I think the safest approach for me is to protect every library call with a lock.

Let's say I have a library object:

dbLib::SomeObject someObject;

Right now I can do something like this:

dbLib::ErrorCode errorCode = 0;
std::list<dbLib::Item> items;
{
    DbLock dbLock;
    errorCode = someObject.someFunction(&items);
} // dbLock goes out of scope

I would like to simplify that to something like this (or even simpler):

dbLib::ErrorCode errorCode =
    protectedCall(someObject, &dbLib::SomeObject::someFunction(&items));

The main advantage of this would be that I won't have to duplicate the interface of dbLib::SomeObject in order to protect each call with a lock.

I'm pretty sure that this is a common pattern/idiom but I don't know its name or what keywords to search for. (Looking at http://www.vincehuston.org/dp/gof_intents.html I think, it's more an idiom than a pattern).

Where do I have to look for more information?

fretje
  • 8,322
  • 2
  • 49
  • 61
foraidt
  • 5,519
  • 5
  • 52
  • 80
  • I don't think it is possible to write it like that. C++ doesn't support quoting expressions (it is something you *would* do in Lisp dialects though). You'll have to move `&items` out of the call too (or use `std::tr1::bind` which gets ugly). However, if you have access to C++0x, you can manufacture something with lambdas. – Alexandre C. Jul 11 '11 at 10:15

6 Answers6

8

You could make protectedCall a template function that takes a functor without arguments (meaning you'd bind the arguments at the call-site), and then creates a scoped lock, calls the functor, and returns its value. For example something like:

template <typename Ret>
Ret protectedCall(boost::function<Ret ()> func)
{
    DbLock lock;
    return func();
}

You'd then call it like this:

dbLib::ErrorCode errorCode = protectedCall(boost::bind(&dbLib::SomeObject::someFunction, &items));

EDIT. In case you're using C++0x, you can use std::function and std::bind instead of the boost equivalents.

reko_t
  • 55,302
  • 10
  • 87
  • 77
5

In C++0x, you can implement some form of decorators:

template <typename F>
auto protect(F&& f) -> decltype(f())
{
    DbLock lock;
    return f();
}

usage:

dbLib::ErrorCode errorCode = protect([&]() 
{
    return someObject.someFunction(&items); 
});
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • This is beautiful; so clean! Sadly, I can't use C++0x, which is why I accepted reko_t's answer. – foraidt Jul 11 '11 at 12:45
  • This is really beautiful. Why does the argument to `protect` need to be an rvalue reference? – Barry Wark Jul 13 '11 at 19:57
  • @Barry: If it is a const reference, then you can only call `f()` if its `operator()` member function is const. Now, if you use a plain (non const lvalue) reference, you cannot write `protect([](){...})` or `protect(std::bind(...))` since the arguments are rvalues. – Alexandre C. Jul 13 '11 at 20:11
  • This is the best reason for C++0x that I've seen. Thanks for the great tip. – Barry Wark Jul 13 '11 at 21:58
  • @Barry: there is a lot of nice stuff in C++0x. What is really nice is that you can combine them together. – Alexandre C. Jul 14 '11 at 00:11
4

From your description this would seem a job for Decorator Pattern.

However, especially in the case of resources, I wouldn't recommend using it.

The reason is that in general these functions tend to scale badly, require higher level (less finegrained) locking for consistency, or return references to internal structures that require the lock to stay locked until all information is read.

Think, e.g. about a DB function that calls a stored procedure that returns a BLOB (stream) or a ref cursor: the streams should not be read outside of the lock.

What to do?

I recommend instead to use the Facade Pattern. Instead of composing your operations directly in terms of DB calls, implement a facade that uses the DB layer; This layer could then manage the locking at exactly the required level (and optimize where needed: you could have the facade be implemented as a thread-local Singleton, and use separate resources, obviating the need for locks, e.g.)

sehe
  • 374,641
  • 47
  • 450
  • 633
  • It doesn't seem like a Decorator pattern. It resembles Python decorators though, but I think the names are misleading here. – Alexandre C. Jul 11 '11 at 10:25
  • The code sample the OP provides is not like a decorator class, if that's what you mean. I wasn't referring to the code sample. – sehe Jul 11 '11 at 10:29
1

The simplest (and still straightforward) solution might be to write a function which returns a proxy for the object. The proxy does the locking and overloads -> to allow calling the object. Here is an example:

#include <cstdio>

template<class T>
 class call_proxy 
 {
  T &item; 
  public:

  call_proxy(T &t) : item(t) { puts("LOCK"); }
  T *operator -> () { return &item; }
  ~call_proxy() { puts("UNLOCK"); }
 };

template<class T>
call_proxy<T> protect(T &t) 
{    
 return call_proxy<T>(t);
}

Here's how to use it:

class Intf
{
 public:
  void function() 
 {
  puts("foo");
 }
};

int main()
{
 Intf a; 
 protect(a)->function(); 
}

The output should be:

LOCK
foo
UNLOCK

If you want the lock to happen before the evaluation of the arguments, then can use this macro:

#define PCALL(X,APPL) (protect(X), (X).APPL)
PCALL(x,x.function());

This evaluates x twice though.

Nordic Mainframe
  • 28,058
  • 10
  • 66
  • 83
1

This article by Andrei Alexandrescu has a pretty interesting article how to create this kind of thin wrapper and combine it with dreaded volatile keyword for thread safety.

Tomek
  • 4,554
  • 1
  • 19
  • 19
  • If I understand the article correctly it won't work for me: I don't have a single object whose access could be synchronized, so this won't work in my case unless I track down all the library functions which actually contain database interaction and make those volatile. *However* this is a very interesting article! – foraidt Jul 12 '11 at 12:55
  • Well, you could wrap around ALL database objects (as I understand that you use objects to access database) which should be a simple task with templates and synchronize on single mutex. Anyway - I'm glad you find the article useful. – Tomek Jul 12 '11 at 18:04
0

Mutex locking is a similar problem. It asked for help here: Need some feedback on how to make a class "thread-safe"

The solution I came up with was a wrapper class that prevents access to the protected object. Access can be obtained via an "accessor" class. The accessor will lock the mutex in its constructor and unlock it on destruction. See the "ThreadSafe" and "Locker" classes in Threading.h for more details.

Community
  • 1
  • 1
StackedCrooked
  • 34,653
  • 44
  • 154
  • 278