Can someone explain this chained templates instantiation? - c++

class close_queue
{};
class dispatcher
{
queue* q;
bool chained;
dispatcher(dispatcher const&)=delete;
dispatcher& operator=(dispatcher const&)=delete;
template<
typename Dispatcher,
typename Msg,
typename Func>
friend class TemplateDispatcher;
void wait_and_dispatch()
{
for(;;)
{
auto msg=q->wait_and_pop();
dispatch(msg);
}
}
bool dispatch(
std::shared_ptr<message_base> const& msg)
{
if(dynamic_cast<wrapped_message<close_queue>*>(msg.get()))
{
throw close_queue();
}
return false;
}
public:
dispatcher(dispatcher&& other):
q(other.q),chained(other.chained)
{
other.chained=true;
}
explicit dispatcher(queue* q_):
q(q_),chained(false)
{}
template<typename Message,typename Func>
TemplateDispatcher<dispatcher,Message,Func>
handle(Func&& f)
{
return TemplateDispatcher<dispatcher,Message,Func>(
q,this,std::forward<Func>(f));
}
~dispatcher() noexcept(false)
{
if(!chained)
{
wait_and_dispatch();
}
}
};
class receiver
{
queue q;
public:
sender operator()()
{
return sender(&q);
}
dispatcher wait()
{
return dispatcher(&q);
}
};
template<typename PreviousDispatcher,typename Msg,typename Func>
class TemplateDispatcher
{
queue* q;
PreviousDispatcher* prev;
Func f;
bool chained;
TemplateDispatcher(TemplateDispatcher const&)=delete;
TemplateDispatcher& operator=(TemplateDispatcher const&)=delete;
template<typename Dispatcher,typename OtherMsg,typename OtherFunc>
friend class TemplateDispatcher;
void wait_and_dispatch()
{
for(;;)
{
auto msg=q->wait_and_pop();
if(dispatch(msg))
break;
}
}
bool dispatch(std::shared_ptr<message_base> const& msg)
{
if(wrapped_message<Msg>* wrapper=
dynamic_cast<wrapped_message<Msg>*>(msg.get()))
{
f(wrapper->contents);
return true;
}
else
{
return prev->dispatch(msg);
}
}
public:
TemplateDispatcher(TemplateDispatcher&& other):
q(other.q),prev(other.prev),f(std::move(other.f)),
chained(other.chained)
{
other.chained=true;
}
TemplateDispatcher(queue* q_,PreviousDispatcher* prev_,Func&& f_):
q(q_),prev(prev_),f(std::forward<Func>(f_)),chained(false)
{
prev_->chained=true;
}
template<typename OtherMsg,typename OtherFunc>
TemplateDispatcher<TemplateDispatcher,OtherMsg,OtherFunc>
handle(OtherFunc&& of)
{
return TemplateDispatcher<
TemplateDispatcher,OtherMsg,OtherFunc>(
q,this,std::forward<OtherFunc>(of));
}
~TemplateDispatcher() noexcept(false)
{
if(!chained)
{
wait_and_dispatch();
}
}
};
class bank_machine
{
messaging::receiver incoming;
public:
bank_machine():
void run()
{
try
{
for(;;)
{
incoming.wait()
.handle<verify_pin>(
[&](verify_pin const& msg)
{
}
)
.handle<withdraw>(
[&](withdraw const& msg)
{
}
)
.handle<get_balance>(
[&](get_balance const& msg)
{
msg.atm_queue.send(::balance(balance));
}
)
.handle<withdrawal_processed>(
[&](withdrawal_processed const& msg)
{
}
)
.handle<cancel_withdrawal>(
[&](cancel_withdrawal const& msg)
{
}
);
}
}
}
};
The code above is a snippet from
C++ Concurrency in Action.
and I was wondering if someone can explain, what looks like, chained template instantiation inside bank_machine::run()? Why is it that we can we have a long chain of handle<some_type>( ...).handle<some_type>( ...).handle<some_type>( ...) . If you could point me to some resources and also correct any missuses of nomenclature I would appreciate it.
Cheers!

Why is it that we can we have a long chain of handle<some_type>( ...).handle<some_type>( ...).handle<some_type>( ...)?
For the same reason that you can chain any operator, e.g.
a + b + c + ...
works, so long as a + b returns an object that can be used as the left-hand side of operator+ with c as the right hand side.
In your example
handle<some_type>(...)
must return an object that has a member access operator . that can be invoked on it, where the member itself can be a handle<some_other_type> that can then be invoked, and so on.

I studied this example code snippet as well and used it in some of my own projects so I wanted to understand it in depth. This my best bet:
Everytime you call
handle
on the temporary dispatcher-object constructed by
incoming.wait()
a temporary object of type TemplateDispatcher is created. The magic happens in the destructor:
the last object to be created will be destroyed first and this will trigger the call to
wait_and_dispatch()
This makes the current thread of execution which is executing this line of code wait for a message to arrive at the message queue (it will sleep, besides spurious wakeups, as the code for dequeueing messages involves condition_variables and associated mutexes). If a message arrives, the thread of execution will check if it is able to deal with the message type and else, if this is not the case, delegate the call to the previously chained TemplateDispatcher or dispatcher-object. When this call is resolved, the destruction of the temporary objects will conclude and due to the
for(;;)
the thread will continue to wait for incoming messages in the same manner, again, until a close_queue-message arrives at the queue and will trigger an exception thrown in the temporary dispatcher-object's code, exactly in:
dispatcher::dispatch()
The TemplateDispatcher-objects created by calls to
handle
will not deal with close_queue-message objects and will therefore delegate any objects of this type to their predecessor in the call chain (dispatcher-class should be the only class that can deal with close_queue-objects) and it will finally be delegated to the dispatcher-object which will then trigger the exception.
The delegation of message objects that are not handled by a certain TemplateDispatcher-objects (identified by the template parameters on instantation in the handle-calls) are delegated to the previous TemplateDispatcher-object in the method
dispatch
of class TemplateDispatcher. A dynamic cast is used to determine if the current TemplateDispatcher-object has to deal with the arrived message.

Related

Turning callback into coroutine while keeping symmetric transfer

Based on the answer of Turning a function call which takes a callback into a coroutine I'm able to come up with a my version of a generic CallbackAwaiter class that I can inherent and wait for callbacks. However I can't figure out how to make it support symmetric transfer. Causing stack overflow in certain cases (mostly in complicated business logic). My code looks like this
template <typename T>
struct CallbackAwaiter
{
bool await_ready() noexcept
{
return false;
}
const T &await_resume() const noexcept(false)
{
assert(result_.has_value() == true || exception_ != nullptr);
if (exception_)
std::rethrow_exception(exception_);
return result_.value();
}
private:
optional<T> result_;
std::exception_ptr exception_{nullptr};
protected:
void setException(const std::exception_ptr &e)
{
exception_ = e;
}
void setValue(const T &v)
{
result_.emplace(v);
}
void setValue(T &&v)
{
result_.emplace(std::move(v));
}
};
// For example. I can inherent the class and fill in `await_suspend` to convert callbacks into coroutines.
struct SQLAwaiter : CallbackAwaiter<std::map<std::string, std::string>>
{
...
void await_suspend(std::coroutine_handle<> handle)
{
dbClient->runSQL(..., [handle](){
...
setValue(...);
handle.resume();
});
}
};
This works. But by calling handle.resume() manually I don't support symmetric transfer. Which stack overflows after deep corouting resume. So far I tried adding promise_type and using std::noop_coroutine to get symmetric transfer working. For example
std::noop_coroutine_handle await_suspend(std::coroutine_handle<> handle)
{
dbClient->runSQL(..., [handle](){
...
setValue(...);
handle.resume();
});
return std::noop_coroutine{};
}
// and
struct CallbackAwaiter
{
CallbackAwaiter() : coro_(std::noop_coroutine{}) {}
std::coroutine_handle<promise_type> coro_;
}
But obviously these wouldn't work. Returning noop_coroutine doesn't magically make handle.resume() not take up stack space. Nor does adding promise_type would work as there's no compiler generated coroutine.
I'm out of idea. How can I support symmetric transfer for such case?
In most cases, your existing code
void await_suspend(std::coroutine_handle<> handle)
{
dbClient->runSQL(..., [handle](){
...
setValue(...);
handle.resume();
});
}
should already be fine.
Depending on your implementation of dbClient->runSQL, the callback which calls handle.resume will be executed on a fresh stack, with only the stack frames from the IO multiplexer, and a couple of other dbClient-internal functions on it.
Symmetric transfer is only a concern if your runSQL function calls its callback synchronously. As long as runSQL always calls its callback asynchronously (i.e. on a "fresh" stack), then you are already fine

Where to place an actor within the state pattern?

I have problems finding the right place for an actor and a timer used in a state machine.
I found some inspiration from this site about the state pattern:
State Design Pattern in Modern C++ and created a small example:
Simple door state machine
There might be more transitions possible but I kept it short and simple.
class Door
{
void open() {}
void close() {}
};
Events:
class EventOpenDoor
{
public:
OpenDoor(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
class EventOpenDoorTemporary
{
public:
EventOpenDoorTemporary(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
class EventOpenDoorTimeout
{
public:
EventOpenDoorTimeout(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
class EventCloseDoor
{
public:
EventCloseDoor(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
using Event = std::variant<EventOpenDoor,
EventOpenDoorTemporary,
EventOpenDoorTimeout,
EventCloseDoor>;
States:
class StateClosed {};
class StateOpen {};
class StateTemporaryOpen {};
using State = std::variant<StateClosed,
StateOpen,
StateTemporaryOpen>;
Transitions (not complete):
struct Transitions {
std::optional<State> operator()(StateClosed &s, const EventOpenDoor &e) {
if (e.m_pDoor)
{
e.m_pDoor->open();
}
auto newState = StateOpen{};
return newState;
}
std::optional<State> operator()(StateClosed &s, const EventOpenDoorTemporary &e) {
if (e.m_pDoor)
{
e.m_pDoor->open();
**// start timer here?**
}
auto newState = StateOpen{};
return newState;
}
std::optional<State> operator()(StateTemporaryOpen &s, const EventOpenDoorTimeout &e) {
if (e.m_pDoor)
{
e.m_pDoor->close();
}
auto newState = StateOpen{};
return newState;
}
std::optional<State> operator()(StateTemporaryOpen &s, const EventOpenDoor &e) {
if (e.m_pDoor)
{
e.m_pDoor->open();
**// stop active timer here?**
}
auto newState = StateOpen{};
return newState;
}
/* --- default ---------------- */
template <typename State_t, typename Event_t>
std::optional<State> operator()(State_t &s, const Event_t &e) const {
// "Unknown transition!";
return std::nullopt;
}
};
Door controller:
template <typename StateVariant, typename EventVariant, typename Transitions>
class DoorController {
StateVariant m_curr_state;
void dispatch(const EventVariant &Event)
{
std::optional<StateVariant> new_state = visit(Transitions{this}, m_curr_state, Event);
if (new_state)
{
m_curr_state = *move(new_state);
}
}
public:
template <typename... Events>
void handle(Events... e)
{ (dispatch(e), ...); }
void setState(StateVariant s)
{
m_curr_state = s;
}
};
The events can be triggered by a client which holds an instance to the "DoorController"
door_controller->handle(EventOpenDoor{door*});
In the events I pass a pointer to the door itself so it's available in the transitions. The door is operated within the transitons only.
I have problems now with modelling the 20s timeout/timer. Where to have such a timer, which triggers the transition to close the door?
Having a timer within the door instance means, I have a circular dependency, because in case of a timeout it needs to call "handle()" of the "door_controller".
I can break the circular dependency with a forward declarations.
But is there a better solution?
Maybe I have modelled it not well. I'm open to improving suggetions.
Thanks a lot!
This isn't going to be the best answer, but I have more questions than answers.
Some of your choices seem odd. I presume there's a complicated reason why you're storing state based on a variant rather than using an enum class State{}, for instance.
I also get nervous when I see raw pointers in modern C++. I'd feel a whole lot better with smart pointers.
When I've done state machines, the events I can handle always subclass from a common Event class -- or I might even just use a single class and give it as many distinct data fields are required for the things that I need to handle. It's a little odd that you use unrelated classes and depend on a dispatch method. Does that even work? Aren't you pushing objects onto an event queue? How do you end up calling that dispatch method with random objects?
You don't show your event loop, but maybe you have a state machine without an event loop. Is it a state machine then? Or maybe you didn't show it. Maybe you can have a state machine without an event loop, but I thought the two concepts were tied together.

Remove while iterating std::vector (indirectly)

This question has been asked multiple times but mine is a slightly different case. Say I have a std::vector of observers which I notify when a certain event happens:
void SomeClass::doThing() {
// do things ...
// notify observers
for (auto* o : mObservers) {
o->thingHappened();
}
}
What if in the implementation of thingHappened the observer calls a method in SomeClass to remove itself from the observers? What are some of the best ways to handle this?
One possibility is to make a copy of mObservers before the for loop and use it instead, but the extra copy can be wasteful.
Another possibility is to delegate changes to the array to be run after the loop is finished, perhaps setting a lock (just a boolean) before the loop starts and while this lock is set, the methods that mutate the vector delegate themselves to be called after the loop is done when lock is set to false (could be done with a vector of lambdas... quite cumbersome).
If you have control over the signature of thingHappened(), you can change it to return a bool indicating whether it should be removed. Then, you can remove all the values which return true (or false; depends on the semantics you want).
Luckily for us, std::remove_if and std::partition are guaranteed to call the predicate exactly once per object in the range.
void SomeClass::doThing() {
// do things ...
// notify observers
auto newEnd = std::remove_if(mObservers.begin(), mObservers.end(), [](auto *o) {
return o->thingHappened();
});
// assuming mObservers is a vector
mObservers.erase(newEnd, mObservers.end());
}
One way to work around this is to change the data structure. With a std::list the removal of a element only invalidates iterators/references/pointers to that element. Since the rest of the list remains intact all we need to do is get an iterator to the next element before we process the current one. That would look like
for (auto it = the_list.begin(); it != the_list.end();)
{
auto next = std::next(it);
it->call_the_possibly_removing_function();
it = next;
}
What if in the implementation of thingHappened the observer calls a method in SomeClass to remove itself from the observers? What are some of the best ways to handle this?
The following method has worked for me in the past.
Note that your are going to iterate over the observers.
When a client requests to remove an observer to be removed, check whether you are in the middle of iterating over the observers. If you are, set it aside in another vector. If not, remove it from the observers.
After you are done iterating over the observers, remove all the observers that need to be removed.
Note that you are done iterating over the observers.
void SomeClass::removeObserver(Observer* o) {
if ( this->isIterating )
{
observersToRemove.push_back(o);
}
else
{
// Code for real removal of the observer
}
}
void SomeClass::doThing() {
this->isIterating = true;
for (auto* o : mObservers) {
o->thingHappened();
}
for ( auto* o : observersToRemove )
{
// Code for real removal of the observer
}
observersToRemove.clear();
this->isIterating = false;
}
R Sahu's answer provides a flexible technique for solving this problem. The one thing that concerns me about it is the introduction of several variables that you have to manage. However, it's totally possible to wrap the functionality in a utility class.
Here's a sketch of what you could do:
#include <functional>
#include <utility>
#include <vector>
// Note that this is not threadsafe
template <typename Type>
class MutableLock {
bool locked = false;
Type value;
// std::function gives us a more general action,
// but it does come at a cost; you might want to consider using
// other techniques.
std::vector<std::function<void(Type&)>> actions;
public:
class AutoLocker {
MutableLock& lock;
friend class MutableLock<Type>;
explicit AutoLocker(MutableLock& lock)
: lock{ lock }
{
}
public:
~AutoLocker()
{
lock.unlock();
}
};
MutableLock() = default;
// The [[nodiscard]] is a C++17 attribute that
// would help enforce using this function appropriately
[[nodiscard]] AutoLocker lock()
{
locked = true;
return AutoLocker{ *this };
}
void unlock()
{
for (auto const& action : actions) {
action(value);
}
actions.clear();
locked = false;
}
template <typename F>
void action(F&& f)
{
if (!locked) {
f(value);
} else {
actions.emplace_back(std::forward<F>(f));
}
}
// There needs to be some way to expose the value
// not under the lock (so that we can use it when
// we call `lock()`).
//
// Even if your `Type` is not a range, this would
// be fine, as member functions of a template class
// aren't instantiated unless you call them.
//
// However, you may want to expose other ways to
// access the value
auto begin() { return std::begin(value); }
auto end() { return std::end(value); }
auto begin() const { return std::begin(value); }
auto end() const { return std::end(value); }
};
Using it would look something like this:
#include <algorithm>
#include <iostream>
class Observer {
public:
virtual void thingHappened() = 0;
protected:
~Observer() = default;
};
class SomeClass {
MutableLock<std::vector<Observer*>> observers;
public:
void addObserver(Observer* observer)
{
observers.action([observer](auto& observers) {
observers.push_back(observer);
});
}
void remove(Observer const* observer)
{
observers.action([observer](auto& observers) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
});
}
void doSomething()
{
auto lock = observers.lock();
for (auto* observer : observers) {
observer->thingHappened();
}
// when `lock` goes out of scope, we automatically unlock `observers` and
// apply any actions that were built up
}
};
class Observer1 : public Observer {
public:
SomeClass* thing;
void thingHappened() override
{
std::cout << "thing 1\n";
thing->remove(this);
}
};
int main()
{
SomeClass thing;
Observer1 obs;
obs.thing = &thing;
thing.addObserver(&obs);
thing.doSomething();
thing.doSomething();
}
On Coliru

Run loop in `std::future` until destruction - idiomatic way?

I want a member std::future<void> to continuously call a function inside a loop until the parent object is destroyed.
My current solution involves wrapping the future in a class with a boolean flag and setting the flag to false on destruction.
class Wrapper
{
std::future<void> fut;
bool wrapperAlive{true};
public:
Wrapper() : fut{std::async(std::launch::async, [this]
{
while(wrapperAlive) doSomething();
})} { }
~Wrapper()
{
wrapperAlive = false;
}
};
Is there a more idiomatic way of doing this?
This is a data-race free version of your code:
class Wrapper {
std::atomic<bool> wrapperAlive{true}; // construct flag first!
std::future<void> fut;
public:
Wrapper() :
fut{std::async(std::launch::async, [this]
{
while(wrapperAlive)
doSomething();
}
)}
{}
~Wrapper() {
wrapperAlive = false;
fut.get(); // block, so it sees wrapperAlive before it is destroyed.
}
};
the next thing I'd do is write:
template<class F>
struct repeat_async_t {
F f;
// ...
};
using repeat_async = repeat_async_t<std::function<void()>>;
template<class F>
repeat_async_t<std::decay_t<F>> make_repeat_async(F&&f){
return {std::forward<F>(f)};
}
which takes a task to repeat forever, and bundle it up in there, rather than mixing the flow logic with what is executed logic.
At this point, we will probably want to add in an abort method.
Finally, it is very rarely a good idea to busy-loop a thread. So we'd add in some kind of wait-for-more-data-to-consume system.
And it ends up looking a lot different than your code.

Remove the defines in this safeguard mutex example

-edit- i cant experiment ATM but will tonight. I am thinking maybe a typedef can be used to hold mut and can be used to declare a var. But my initial thought is typedefs don't play nice with templates so i'll have to check later tonight (for now, to class)
I was looking at this piece of code shown below and i was wondering how it might be possible to implement without using defines.
Since I cant compile the code (i don't have any mutex/multithreading libs currently installed) i'll just look at the code and think it out.
It seems like one can completely implement PROTECTED_WITH by inheriting a template class. The problem is now PROTECTED_MEMBER. It uses a name with ## to create a variable. This isnt much of a problem because we create a class which holds the variable with the () operator to make it appear as a function. However accessing is_held() the problem as i would like not to pass this or mut_ in.
My gut says with out of the box thinking its possible to solve this without defines and without passing in to each variable a this, function ptr or reference. I'll allow everyone to cheat and use c++0x features.
template<typename Mutex>
class TestableMutex {
public:
void lock() { m.lock(); id = this_thread::get_id(); }
void unlock() { id = 0; m.unlock(); }
bool try_lock() { bool b = m.try_lock();
if( b ) id = this_thread::get_id();
return b; }
bool is_held() { return id == this_thread::get_id(); }
private:
Mutex m;
atomic<thread::id> id;
// for recursive mutexes, add a count
};
#define PROTECTED_WITH(MutType) \
public: void lock() { mut_.lock(); } \
public: bool try_lock() { return mut_.try_lock(); } \
public: void unlock() { mut_.unlock(); } \
private: TestableMutex<MutType> mut_;
#define PROTECTED_MEMBER(Type,name) \
public: Type& name() { assert(mut_.is_held()); return name##_; } \
private: Type name##_;
struct MyData {
PROTECTED_WITH( some_mutex_type );
PROTECTED_MEMBER( vector<int>, v );
PROTECTED_MEMBER( Widget*, w );
};
You can use an explicit specialization containing using declarations to list the objects protected by the mutex. Then use a base class to "pass" the access out to the user via operator->, so object->member (with object not being a pointer) performs the mutex assertion.
This is easier done than said:
// Imagine that the members of this class must be locked by the mutex.
class a : public expose_locked_by_arrow< a > {
protected:
int i;
void f();
};
// Declare which members are conditionally locked. Pretty simple and idiomatic.
template<>
struct member_expose< a > : a {
using a::i;
using a::f;
};
#include <iostream>
// Access mutex-locked members with ->
int main() {
a x;
x->i = 5;
a const y( x );
std::cout << y->i << '\n';
}
The library code:
// This template is specialized for each mutex protection client.
template< class >
struct member_expose;
// Base class provides mutex; parameter is derived class (CRTP).
template< class c >
struct expose_locked_by_arrow {
member_expose< c > *
operator->() {
assert ( expose_lock_mutex.is_held() );
return static_cast< member_expose< c > * >( this );
}
member_expose< c > const *
operator->() const {
assert ( expose_lock_mutex.is_held() );
return static_cast< member_expose< c > const * >( this );
}
expose_locked_by_arrow( mutex const &m = mutex() )
: expose_lock_mutex( m ) {}
protected:
mutex expose_lock_mutex;
};
See it run.
The #defines aren't providing any protection as such, rather they are just reducing the amount of typing you'd have to do (in turn, they make sure all the "protected" members have the proper code in place).
There isn't a way that I am aware of to avoid having to put the checks into each getter function - and locking the whole object, as they are returning references to data stored within the protected object.
If however, they could all be returned by value (or not returning anything at all), then you could use a container that locks everything using a proxy object, something like the following (this could probably be done better, i've just quickly hacked it together):
#include <iostream>
struct Mutex
{
void lock()
{
std::cout << "Mutex::lock" << std::endl;
}
void unlock()
{
std::cout << "Mutex::unlock" << std::endl;
}
};
template <class Object>
class ThreadSafeObject
{
mutable Mutex d_mutex;
Object d_object;
public:
struct Proxy
{
mutable Mutex *d_mutex;
Object *d_object;
Proxy(Mutex *mutex, Object *object)
: d_mutex(mutex)
, d_object(object)
{
d_mutex->lock();
}
Proxy(const Proxy& proxy)
: d_mutex(proxy.d_mutex)
, d_object(proxy.d_object)
{
proxy.d_mutex = NULL;
}
~Proxy()
{
if (d_mutex)
{
d_mutex->unlock();
}
}
Object *operator->()
{
return d_object;
}
};
struct ConstProxy
{
mutable Mutex *d_mutex;
const Object *d_object;
ConstProxy(Mutex *mutex, const Object *object)
: d_mutex(mutex)
, d_object(object)
{
d_mutex->lock();
}
ConstProxy(const ConstProxy& proxy)
: d_mutex(proxy.d_mutex)
, d_object(proxy.d_object)
{
proxy.d_mutex = NULL;
}
~ConstProxy()
{
if (d_mutex)
{
d_mutex->unlock();
}
}
const Object *operator->() const
{
return d_object;
}
};
Proxy operator->()
{
return Proxy(&d_mutex, &d_object);
}
ConstProxy operator->() const
{
return ConstProxy(&d_mutex, &d_object);
}
};
struct Foo
{
void foo()
{
std::cout << "Foo::foo" << std::endl;
}
};
int main()
{
ThreadSafeObject<Foo> myFoo;
myFoo->foo();
return 0;
}
Which uses the operator->() trick (when operator-> doesnt reutrn a pointer type, the compiler will keep calling operator-> on the returned values until eventually a regular pointer type is returned) and gives the following output:
Mutex::lock
Foo::foo
Mutex::unlock
Generally speaking though, an object that needs to be used by multiple threads shouldn't be exposing its internals like that, it would be safer to have it accept parameters and use its internal values to act on them.