-
Notifications
You must be signed in to change notification settings - Fork 3k
CriticalSectionLock class improvement #5420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CriticalSectionLock class improvement #5420
Conversation
platform/CriticalSectionLock.h
Outdated
core_util_critical_section_exit(); | ||
if (_locked) { | ||
core_util_critical_section_exit(); | ||
_locked = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_locked
doesn't need setting while destructing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
It might be worth adding an |
2220012
to
0aa6680
Compare
platform/CriticalSectionLock.h
Outdated
} | ||
|
||
/** Mark the start of a critical section | ||
* | ||
*/ | ||
void lock() | ||
{ | ||
core_util_critical_section_enter(); | ||
MBED_ASSERT(false == _locked); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is recursive locking not supported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to prevent user from lock/unlock
mismatch e.g. calling more locks then unlocks and vice versa. The recursion could be handled by one CriticalSectionLock instance on each level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mismatch is taken care of in core_util_critical_section_enter / core_util_critical_section_exit functions.
Counter interrupt_enable_counter
is maintained to keep track
platform/CriticalSectionLock.h
Outdated
{ | ||
core_util_critical_section_enter(); | ||
if (lock) { | ||
core_util_critical_section_enter(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change blurs the line even further between a regular lock and an RAII class so I would argue this is the wrong direction to be taking the code.
platform/CriticalSectionLock.h
Outdated
core_util_critical_section_exit(); | ||
if (_locked) { | ||
core_util_critical_section_exit(); | ||
} | ||
} | ||
|
||
/** Mark the start of a critical section | ||
* | ||
*/ | ||
void lock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @deepikabhavnani mentioned, I would recommend either making this static or deprecate this and create another class for non-raii locking.
This. If you look at |
Was initially skeptical about this, but given the current state of CriticalSectionLock, I think this is good. (I would have probably originally made it pure RAII, as manual control is a tad fancy.) As it stands, the visible lock() and unlock() methods are dangerous, and this makes them work safely in an RAII+combo, akin to unique_lock. There should be no need for recursion support within this object itself, as it's designed for local scope - anyone calling lock() or unlock() must surely know what its current state is (which is a separate issue from the total crititcal count). In the new example, the last unlock is redundant - leave it out, maybe? |
That is what we were discussing the last week, if we should not deprecate these 2 methods, made it
What about this proposal from above (not having the combo) :
|
Summing up the discussion:
|
Thanks for the summary, this should help to finalize this change. I want to hear the rest as I did not find a conclusion for this.
This one (scoped lock) might be sufficient for most of use case, might not? @pan- Your conclusion for |
@0xc0170 @maciejbocianski If it's scoped and only meant to be used as an RAII guard, why not deprecate the existing type and create a new one with a name which expose that property: |
Updated plan:
|
any comments? |
Sounds sensible the proposal. What others think? You can add additional commits that would add these changes ? |
Sounds fine to me. But some thoughts:
Don't know how much we like templates - would it be worth templating so we can do (Would the template version have to actually be I do think a more-advanced manually-controllable |
That's a reasonable reason to use a class. @maciejbocianski The class should implement the BasicLockable concept.
The template implementation avoid code duplication which is good from maintenance perspective as well as extensibility: if a type implement the BasicLockable concept then it can be used in a scoped lock. There is nothing preventing us to create alias for common scoped locks such as the one for the critical section or the mutex and use them in code. What do you think ?
I would be interested to have a common pattern regards the reporting of errors occurred during object construction; this may be an issue for all our RAII guards (and other kind of objects). I also wonder if the Lockable concepts should not be reworked a bit to take into account the fact that we don't throw exceptions to signal errors.
Not exactly, the parameter in input should outlive the lock therefore it cannot be constructed at the call site: CriticalSection critical_section;
ScopedLock<CriticalSection> lock(critical_section); One solution would be to have a global critical_section object and an alias the ScopedLock (Or two different classes as you proposed ...). |
I think failure of a lock() claim on a mutex is fatal enough to be worth Without looking, I bet there's no actual recovery from failure of non-timed Mutex::lock() in the system anywhere - only asserts, or maybe passing up an error code to another layer with no checking or recovery.
True. Make the template, and have typedefs or whatever for the Mutex and CriticalSection forms.
Does it matter in this case, where To save C++ verbosity, I suppose we can give ScopedCriticalSectionLock a default constructor that grabs the default object, thus:
(I hope that at -O2 that object gets optimised out...) |
Yes it does matter because if the signature of lock is: Even if it was possible due to the static nature of the critical section lock; I don't think it would be a good thing to encourage such construct because some users may blindly reproduce the pattern invalid pattern with other lockable objects. |
Below my proposition
The only drawback of this solution I can see is that using global lock object requires class instantiation with reference to template <typename LockType>
class ScopedLock {
public:
ScopedLock()
{
_lock.lock();
}
ScopedLock(LockType lock): _lock(lock)
{
_lock.lock();
}
~ScopedLock()
{
_lock.unlock();
}
private:
LockType _lock;
};
class CriticalSection : private mbed::NonCopyable<CriticalSection> {
public:
void lock()
{
core_util_critical_section_enter();
}
void unlock()
{
core_util_critical_section_exit();
}
};
// usage
CriticalSection g_cs;
Mutex g_mutex;
{
// local/private lock objects
ScopedLock<CriticalSection> cs_lock;
ScopedLock<Mutex> mutex_lock;
}
{
// global lock objects
ScopedLock<CriticalSection&> cs_lock(g_cs);
ScopedLock<Mutex&> mutex_lock(g_mutex);
} |
The mission of scoped lock is to lock at construction time unlock at destruction time an existing I would amend the proposal the following way: // Mark the type as non copyable, it is just a guard which lock at construction
// and unlock at destruction.
template <typename Lockable>
class ScopedLock : private mbed::NonCopyable<ScopedLock<Lockable> > {
public:
ScopedLock(Lockable& lockable): _lockable(lockable)
{
_lockable.lock();
}
// Note: remove virtual specifier, it is not desired to inherit from this class in
// a polymorphic way.
~ScopedLock()
{
_lockable.unlock();
}
private:
Lockable& _lockable;
};
class CriticalSection : private mbed::NonCopyable<CriticalSection> {
public:
// this can be static; access operators (. and ->) will continue to work
static void lock()
{
core_util_critical_section_enter();
}
static void unlock()
{
core_util_critical_section_exit();
}
};
// Typedef for the mutex lock
typedef ScopedLock<Mutex> ScopedMutexLock;
// Note: inherit from CriticalSection and ScopedLock to use empty base optimization
class ScopedCriticalSectionLock : private CriticalSection, private ScopedLock<CriticalSection> {
public:
ScopedCriticalSectionLock() :
CriticalSection(),
ScopedLock<CriticalSection>(static_cast<CriticalSection&>(*this)) {
}
};
// usage
void foo(Mutex& m) {
ScopedMutexLock lock(m);
}
void bar() {
ScopedCriticalSectionLock lock;
}
template<typename Lockable>
void baz(Lockable& lockable) {
ScopedLock<Lockable> lock(lockable);
} At the end, user code would mostly rely on |
@pan-, that looks good to me - tried compiling with it briefly.
|
@pan- Your solution looks good. BTW I also noticed that size of |
Size of And even without optimisation, it's only on the stack anyway. |
So I will implement @pan- proposition and will move |
Triggered uvision CI, should finish soon This will become ready for integration. @maciejbocianski thanks for the improving critical sections classes. This will require update in the docs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont understand why we went the path of deprecating entire classes that are just 1 release old. The initial PR intention was great but is disappointing to see how we got to the final solution. I would have expected deprecation on local lock/unlock and static members compared to what we ended up with here. That would have demonstrated good engineering hygiene and discipline.
This PR is a great example of lots of bikesheding and wasted time. We need to commit to the code and fix things rather than always looking elsewhere to how it could/should be. This will result in less effort twiddling such trivial things, unplanned things.
To preserve work done in this PR for future use this PR will be closed soon. |
Okay, that's a no on renaming |
@maciejbocianski Can we close this PR and create a tracking issue for this improvements that were made here? |
I guess we can still do something useful after #5621, but it will be much less coherent - the CriticalSectionLock will now end up being a special case, rather than a I would suggest adding |
So, maybe just continue this PR, dropping all the changes to CriticalSectionLock.h? |
Any comments about the proposed action? |
To be fair I lost the track of what's going on here over a month ago, so opening new PR that limits the noise and confusion would be very much appreciated. That is if it makes sense and original scope changed. |
Description
Several improvements added to make it more versatile:
lock/unlock
usage (see example below)Status
READY
Migrations
NO