0

I will begin with an example. Suppose I need to guard a code with a function inside a mutex. There are two ways of implementing this.

#include <iostream>
#include <vector>
#include <pthread.h>



pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
std::vector<float> myVec;

void threadfunc(int i, float value)
{
  pthread_mutex_lock(&myMutex);
  if(i <= 0 || i > myVec.size())
  {
    pthread_mutex_unlock(&myMutex);
    return;
  }

  if(value < 0)
  {
    pthread_mutex_unlock(&myMutex);
    return;
  }

  myVec[i] += value;
  pthread_mutex_unlock(&myMutex);
  return;
}

class AUTOMUTEX
{
  private:
    pthread_mutex_t *mMutex;
  public:
    AUTOMUTEX(pthread_mutex_t *mutex): mMutex(mutex)
  {
    pthread_mutex_lock(mMutex);
  }

    ~AUTOMUTEX()
    {
      pthread_mutex_unlock(mMutex);
    }
};


void threadfunc_autolock(int i, float value)
{
  AUTOMUTEX  autoMutex(&myMutex);
  if(i <= 0 || i > myVec.size())
  {
    return;
  }

  if(value < 0)
  {
    return;
  }

  myVec[i] += value;
  return;
}

int  main()
{
  threadfunc_autolock(5, 10);
  threadfunc(0, 7);
  return 1;
}

As it is clear from the example threadfunc autolock is better implementation as calling pthread_mutex_unlock function return is taken care by destructor call to AUTOMUTEX (C++ 11 thread has support for this. So we don't need our own implementation of AUTOMUTEX if we are using C++11 thread library). Is there a way we can achieve this without implementing a wrapper class each time we need to do this with some set/reset function pair. Does boost or C++ 11 have some predefined template class with which we can achieve the behaviour of AUTOMUTEX for any such "set/reset" sort of function. This is really helpful for functions with multiple points of return. In other words does boost/C++ provide a class with the following behaviour.

//sample code not compilable.
template <class T, class Y>
class myAuto
{
  myAuto()
{ 
   T();
}
  ~myAuto()
{
  Y();
};
  • Are you specifically looking for a mutex locking/unlocking mechanism or a general calling of two unrelated functions on entry and on exit? – Niall Oct 09 '15 at 06:30
  • The [Boost thread library](http://www.boost.org/doc/libs/1_59_0/doc/html/thread.html) have had scoped locks for a long time. – Some programmer dude Oct 09 '15 at 06:32

6 Answers6

3

You may write your own geneirc RAII class, something like:

class Finally
{
public:
    explicit Finally(std::function<void()> f) : mF(f) {}
    ~Finally() noexcept() {
        try
        {
            mF();
        } catch (...) {
            // Handle error.
        } 
    }

    Finally(const Finally&) = delete;
    Finally(Finally&&) = delete;

    Finally& operator=(const Finally&) = delete;
    Finally& operator=(Finally&&) = delete;

private:
    std::function<void()> mF;
};

Usage:

{
    pthread_mutex_lock(&myMutex);
    Finally finally([&](){ pthread_mutex_unlock(&myMutex); });

    //..
}

Even if a dedicated RAII object may be more appropriate in some case (as Mutex).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

You could use something like ScopeGuard. (Now somewhat old-fashioned.)

But given how easy and clear it is to construct a specific RAII wrapper for each resource type I would normally do that.

(I don't think boost ever adopted anything like ScopeGuard. With std::function, lambdas and so on it's easy to do your own.)

Community
  • 1
  • 1
Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
  • 1
    Boost has ScopeExit: http://www.boost.org/doc/libs/1_59_0/libs/scope_exit/doc/html/index.html. – beerboy Oct 12 '15 at 20:34
0

There is a proposal for a generic scope guard to be included in the next C++ standard, and I think it is accepted. You can find an implementation here, together with a link to the reference paper.

In principle, it is similar to the classical ScopeGuard, but it also provides some special cases e.g. for C-like file APIs.

Jens
  • 9,058
  • 2
  • 26
  • 43
0

What's wrong with writing your own generic resource wrapper?

template <typename Res, typename Fn = std::function<void(Res*)>>
class resource_mgr
{
    Res* resource;
    Fn initialize, finalize;

public:
    resource_mgr (Res* r, Fn i, Fn f)
    : resource(r),
      initialize(i),
      finalize(f)
    {
        initialize(resource);
    }

    resource_mgr (resource_mgr const&) = delete;
    resource_mgr (resource_mgr&&)      = delete;

    resource_mgr const& operator = (resource_mgr const&) = delete;
    resource_mgr const& operator = (resource_mgr&&)      = delete;

    ~resource_mgr
    {
        try
        {
            finalize(resource);
        }
        catch(...)
        {
            std::cerr << "Uh-oh!"
        }
    }
};

You can keep it simple or go wild on something like this -- use smart pointers, define move operations, add support for custom error handlers, etc. You might use it like this:

void threadfunc_autolock(int i, float value)
{
    resource_mgr<mutex_t>  autoMutex (
        &myMutex,
        [](auto* p) { if (!pthread_mutex_lock(p)) throw Something(); },
        [](auto* p) { if (!pthread_mutex_unlock(p)) throw Something(); }
    );

    /* . . . */
}
acwaters
  • 586
  • 3
  • 6
0

Here's an example using Boost.ScopeExit (untested):

#include <boost/scope_exit.hpp>

...

void threadfunc_autolock(int i, float value)
{
    pthread_mutex_lock(&myMutex);

    BOOST_SCOPE_EXIT(&myMutex) {
        pthread_mutex_unlock(&myMutex);
    } BOOST_SCOPE_EXIT_END

    if(i <= 0 || i > myVec.size())
    {
        return;
    }

    if(value < 0)
    {
        return;
    }

    myVec[i] += value;
}
beerboy
  • 1,304
  • 12
  • 12
0

I know it's an old question, but it seems nobody showed this very compact way exploiting shared_ptr, that can be created with a custom deleter function:

std::shared_ptr<int> a(nullptr, [](int*){ printf("deleted\n"); fflush(stdout); });

Here it's created with a null pointer (not using it for its original purpose); at destruction, your custom function is called on the pointer (ignoring it).

As a plus, you can use it for an actual object that needs special cleaning:

FILE *f = fopen(...);
std::shared_ptr<FILE> a(f, &fclose);
m.alessandrini
  • 317
  • 2
  • 9