0

I never really worked with mutexes before, but i need to control access to protected resources. Looking through the new C++11 stuff, i cooked up this class:

class CMutex
{
public:
    class Lockable
    {
        friend class CMutex;
        std::atomic_flag flag;
    public:
        Lockable()
        {
            flag.clear();
        }
    };
private:
    Lockable * resource;
    CMutex(const CMutex &);
public:
    CMutex(Lockable * l)
    {
        resource = l;
        acquire(l);
    }
    CMutex(Lockable & l)
    {
        resource = &l;
        acquire(l);
    }
    CMutex()
        : resource(nullptr)
    {
    }
    ~CMutex()
    {
        if (resource)
            release(resource);
    }
    void acquire(Lockable * l)
    {
        if (!resource)
            resource = l;
        if (!spinLock(2000, resource))
            //explode here
            return;
    }
    void acquire(Lockable & l)
    {
        acquire(&l);
    }
private:
    void release(Lockable * l)
    {
        if (l)
            l->flag.clear();

    }
    static bool spinLock(int ms, Lockable *  bVal)
    {
        using namespace Misc;
        long start;
        int ret;
    loop:
        start = QuickTime();
        while (bVal->flag.test_and_set()) {
            if ((QuickTime() - start) > ms)
                goto time_out;
            // yield thread
            Delay(0);
        }
        // normal exitpoint
        return true;
        // deadlock occurs
    time_out:
        // handle error ...
    }
}

Usage like so:

class MyClass : public CMutex::lockable
{
    ...
    void doStuff()
    {
        // lock data
        CMutex(this);

        // do stuff
        ...

        // mutex should automagically be RAII released here
    }
    ...
};

First of all, I'm interested in whether this concept actually works how it should (given the implementation of std::atomic etc.)?

Secondly, I noticed that it correctly obtains the lock, however it releases it instantly. I guess i should give the lock a name?

CMutex lock(this);

However, isn't the compiler free to destruct the object before the scope is left as an optimization provided it can guarantee that i wont interact more with the object? This would defeat the purpose of this construct, if i can't guarantee that the destructor only will be called at scope exit.

Regards

Shaggi
  • 1,121
  • 1
  • 9
  • 31
  • `CMutex(this)` is a temporary and only lasts until the end of the full expression (i.e. up to the semicolon) then it gets destroyed. When you give it a name it becomes an automatic variable and is destroyed at the end of the scope. – Simple Mar 07 '14 at 18:17
  • 4
    There already is [`std::lock_guard`](http://en.cppreference.com/w/cpp/thread/lock_guard) in the Standard Library since C++11. – Felix Glas Mar 07 '14 at 18:21
  • @Simple Yes i figured, but how about the last comment (question 3?) -Snps I know, but i might need to extend this to platforms without c++11, this is just a prototype – Shaggi Mar 07 '14 at 18:23
  • @Shaggi the destructor not being called at the end of the scope, and being called earlier, would break the as-if rule because it your code can observe that. The compiler is not allowed to perform that optimisation. – Simple Mar 07 '14 at 18:24
  • 3
    @Shaggi: If you need to use pre-C++11, then you shouldn't use C++11 features. Either implement this class in terms of `std::mutex`, which will do the right thing; or call platform specific locks. Or use boost::thread. Or similar. Inventing your own locks is a recipe for disaster. – Billy ONeal Mar 07 '14 at 18:28
  • @BillyONeal As said this is only a prototype. If C++11 is available, the class will use it, otherwise it will fallback on platform methods (has to be crossplatform) – Shaggi Mar 07 '14 at 18:30
  • @Shaggi: Then why did you implement things in terms of `std::atomic_flag`? It never makes sense to fall back to that. – Billy ONeal Mar 07 '14 at 18:32
  • @BillyONeal As far as i can see, it's guaranteed by the specification to work the i intented? Also, desire to have my own spinlock implementation. – Shaggi Mar 07 '14 at 18:36
  • @Shaggi: 1. I have no idea. 2. That doesn't make any sense. – Billy ONeal Mar 07 '14 at 18:38
  • @BillyONeal 1. Okay. 2. Really? It gives me the possiblity to handle an error if the mutex fails to acquire the resource, possibly avoiding deadlocking the application. Secondly, it might avoid busywaiting by yielding the thread instead - this i cannot control with library implementations. – Shaggi Mar 07 '14 at 18:49
  • `std::mutex` can do that too. I will now mention something about a fine manual. – R. Martinho Fernandes Mar 07 '14 at 19:15
  • are you referring to mutex::try_lock? if i used that, i would have to write a wrapper around the object anyway. – Shaggi Mar 07 '14 at 19:20
  • Wrapper exists already as unique_lock. – R. Martinho Fernandes Mar 07 '14 at 19:22
  • Oh. Well the good thing is that this works well-defined, and if im ever going to change it, i can do it in one place :) – Shaggi Mar 07 '14 at 20:23
  • @Shaggi: I'm not saying that using a wrapper is a bad idea; if you want to wrap it, that's fine. I'm just saying wrap `std::mutex` rather than `std::atomic_flag`. – Billy ONeal Mar 07 '14 at 21:45

2 Answers2

1

No, the compiler is not free to destruct before the scope ends.

Per the C++ Standard section 12.4/10 — for constructed objects with automatic storage duration (3.7.3) when the block in which an object is created exit.

Sanjaya R
  • 6,246
  • 2
  • 17
  • 19
-1

Here is a trick that may come handy for you and it works with all mainstream (VC++, clang, gcc) compilers:

#define APPEND_ID1(id1, id2) id1##id2
#define APPEND_ID2(id1, id2) APPEND_ID1(id1, id2)
#define APPEND_COUNTER(id) APPEND_ID2(id, __COUNTER__)

#define SCOPED_LOCK(lockable) CMutex APPEND_COUNTER(scoped_lock)(lockable)

class MyClass : public CMutex::lockable
{
    ...
    void doStuff()
    {
        // lock data
        SCOPED_LOCK(this);
        // auto-generates a unique name, works even with multiple locks in the same scope..
        SCOPED_LOCK(that);

        // do stuff
        ...

        // mutex should automagically be RAII released here
    }
pasztorpisti
  • 3,760
  • 1
  • 16
  • 26
  • 2
    Doesn't that generate names of the form `__scoped_lockXY` and aren't these names reserved identifiers for compiler implementation? – stefan Mar 07 '14 at 18:42
  • You can replace "\_\_scoped\_lock" with whatever you want. I usually generate names starting with "\_" to hide these names from my VAssistX auto-completion tool. Anyway, starting with some private framework variable/type names with "\_" is quite okay. – pasztorpisti Mar 07 '14 at 18:44
  • 1
    Well it may seem handy to hide these names, but it invokes undefined behaviour: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier/228797#228797 – stefan Mar 07 '14 at 18:49
  • @pasztorpisti: Not really, no. Your toolchain already reserved them. Use a `detail` namespace. – Lightness Races in Orbit Mar 07 '14 at 18:53
  • @stefan Thanks for the info, I didn't know that. Ironically I'm working on massive/hugh codebases and in practice I haven't run into such a problem with any of the major compilers (VC, clang, gcc). Maybe it was just luck with the right combination of underscore count and the case of the letters and/or other factors like scope... :-) OK, I repair the variable name in my answer. – pasztorpisti Mar 07 '14 at 18:56
  • @pasztorpisti Yes, that's the danger. One day a compiler defines a macro using one of your names and _boom_, everything breaks. – stefan Mar 07 '14 at 18:57
  • @stefan macros are usually full-capital, and this seems to be a widely accepted convention. In my opinion it sucks when you need a bible for a language and C++ is worse and worse in this regard. This language would need a serious cleanup but that day will probably never come because of backward compatibility. BTW, this underscore rule thingy is just an unnecessary over-complication in my opinion. Probably this is why it works for me, I can easily imagine that language implementers simply ignore this rule if its possible, they have enough problems anyway in case of C++... – pasztorpisti Mar 07 '14 at 19:04
  • You _don't_ need a "bible". You need to follow the rules of the language, like any other, rather than guessing. Sounds like you need a better book... Reserving leading underscores is hardly an "over-complication". In fact, it's really very simple. – Lightness Races in Orbit Mar 07 '14 at 19:05
  • I'm not guessing. Probably there is no human on earth who knows all rules from the "bible". Not knowing a rule doesn't mean that "you need a better book" and it doesn't affect any important software engineering skills. And in fact, knowing these rules isn't the true weapon of a programmer. The linked rule is also a hidden corner of the "bible" surrounded with '§' characters. In my opinion C++ is a monster language and I use it just because I have to use it for several reasons. – pasztorpisti Mar 07 '14 at 19:13
  • @pasztorpisti: If you are a professional C++ programmer you should absolutely know which identifiers are reserved. It is not "hidden". You learnt from an inadequate source. '§' is the "section" symbol, and conventionally means "section in a document or other text". None of this is rocket science... – Lightness Races in Orbit Mar 07 '14 at 19:18
  • @LightnessRacesinOrbit But there are admittedly a buttload of rules in C++. It's OK to not know everything unless you're willing to learn, just as pasztorpisti is. – stefan Mar 07 '14 at 19:20
  • @stefan: I never claimed you had to know everything. :) But these are the basics, and claiming that they are esoteric is ludicrous. – Lightness Races in Orbit Mar 07 '14 at 19:20
  • @LightnessRacesinOrbit to be honest I've never read a "basic" C++ programmer book desptite programming C/C++ for more than a decade. I've learnt form opensource. Once I started to read the bible but after a few ten pages it was enough. I'm a man of practice anyway and if a language needs thousands of pages just to list pure language features/rules then something isn't OK. In my opinion what you need is being able to read other's code, being able to write easy-to-understand beautiful-simple-stupid code, and being able to spot/fix performance bottlenecks. None of these requires books/bibles. – pasztorpisti Mar 07 '14 at 19:30
  • Ten pages? You're insane. Learn C++. From one of [these](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). If you think you can learn a multi-purpose, multi-paradigm language from ... well, what, I'm not quite sure... but not a book? You're out of your mind. Sounds like you are taking quite a naive view of a complex piece of technology. If complexity isn't your thing, you may be more content with something like PHP or Ruby. – Lightness Races in Orbit Mar 08 '14 at 00:26
  • @LightnessRacesinOrbit Of course I've read C++ books, just not the boring beginner ones and not the bible but in my opinion its still not the books that make you a good programmer. Someone can easily be a very bad programmer despite reading many programmer books. Its also quite possible to pick up good software engineering skills even in the wild without books if someone has the veins. I often use python to automatize things and I like high level stuff but having my roots in assembly/C/C++ I often play around with lower level stuff too. And yea, unnecessary complexity isn't my thing... – pasztorpisti Mar 08 '14 at 00:39
  • @pasztorpisti: Right, well, what you call "boring" is what I call "what _you_ should take the time to read", because apparently you could use a brush-up on the basics. Shrugging them off as "too good for you" is doing you a disservice. If you can't be bothered to learn the basic rules of the language then you are _not_ a good programmer. I don't say this to be mean: it's patently and evidently clear. Perhaps lose the naivety and take the time to allow yourself to learn something. – Lightness Races in Orbit Mar 08 '14 at 00:51
  • I'm sorry but if you think "don't make your own identifiers that conflict with reserved names" is "complexity", then you're in the wrong job. – Lightness Races in Orbit Mar 08 '14 at 00:53
  • I'm also sorry because in practice it wasn't complexity at all as you see, otherwise I would have known about it. I think we define "complexity" differently. It isn't just my code that contains such variables. If they changed compilers not to accept such variables probably a lot of code would break in the world, but that is another story... – pasztorpisti Mar 08 '14 at 01:29
  • @pasztorpisti: If you want to continue referring to "don't use reserved words" as _complexity_, that's certainly your right. Just don't expect me or any other remotely competent C++ professional to hire you. – Lightness Races in Orbit Mar 08 '14 at 01:33
  • You will not hire me and I will not hire you. That's it. We are thinking differently and our opinion on what is important and what isn't "slightly" differs just as our temperament so we probably wouldn't be a good team. My conclusion: Today I've just learned something simple: I've used a wrong naming convention along with many guys in the wild but that hasn't caused anything bad so far in the past 10 years... I don't really feel disappointed or whatever... I go home and drink something cold.... :-) – pasztorpisti Mar 08 '14 at 01:46