BitShares-Core  7.0.2
BitShares blockchain node software and command-line wallet software
Public Member Functions | List of all members
fc::mutex Class Reference

mutex More...

#include <mutex.hpp>

Public Member Functions

 mutex ()
 
 ~mutex ()
 
bool try_lock ()
 
bool try_lock_for (const microseconds &rel_time)
 
bool try_lock_until (const time_point &abs_time)
 
void lock ()
 
void unlock ()
 

Detailed Description

mutex

This mutex has an advantage over boost::mutex in that it does not involve any system calls, even in contention.

Uncontensted access is a simple compare and swap, no delay.

Contested access by different fibers in the same thread simply yields the thread until the lock is available. Actual delay is subject to the cooperative nature of other tasks in the fiber's thread.

Contested access by different threads requires a spin lock while the task is enqueued. Because the enqueue action is well-defined and 'short-lived' time spent 'spinning' should be minimal.

Cooperatively multi-tasked code must still worry about reentrancy. Suppose you have a thread sending a message across a socket, the socket members are thread safe, but the write_message() operation is not rentrant because the context could yield while waiting for a partial write to complete.

If while it has yielded another task in the same thread attempts to write a second message then you will get garbage out as both fibers take turns writing parts of their messages out of the socket.

Example problem:

async(write_message);
async(write_message);
void write_message() {
sock->write(part1); // may yield
sock->write(part2); // may yield
sock->write(part3); // may yield
}

The output could look something like:

part1
part2
part1
part3
part2
part3

What you want to happen is this:

void write_message() {
boost::unique_lock<fc::mutex> lock(sock->write_lock);
sock->write(part1); // may yield
sock->write(part2); // may yield
sock->write(part3); // may yield
}

Now if while writing the first message, someone attempts to write a second message second write will 'queue' behind the first by 'blocking' on the mutex.

As a result we now have to extend the normal discussion on thread-safe vs reentrant.

In the example above, before we added the mutex the code was thread-unsafe After we added the mutex the code became coop-thread-safe, and potentially prempt-thread-safe

To be preempt-thread-safe any operations must be atomic or protected by a lock because the OS could switch you out between any two instructions.

To be coop-thread-safe all operations are 'atomic' unless they span a 'yield'. If they span a yield (such as writing parts of a message), then a mutex is required.

Definition at line 91 of file mutex.hpp.

Constructor & Destructor Documentation

◆ mutex()

fc::mutex::mutex ( )

Definition at line 10 of file mutex.cpp.

◆ ~mutex()

fc::mutex::~mutex ( )

Definition at line 15 of file mutex.cpp.

Member Function Documentation

◆ lock()

void fc::mutex::lock ( )

Definition at line 136 of file mutex.cpp.

◆ try_lock()

bool fc::mutex::try_lock ( )

A mutex is considered to hold the lock when the current context is the tail in the wait queue.

Definition at line 83 of file mutex.cpp.

◆ try_lock_for()

bool fc::mutex::try_lock_for ( const microseconds rel_time)

◆ try_lock_until()

bool fc::mutex::try_lock_until ( const time_point abs_time)

Definition at line 101 of file mutex.cpp.

◆ unlock()

void fc::mutex::unlock ( )

Definition at line 194 of file mutex.cpp.


The documentation for this class was generated from the following files:
fc::mutex::lock
void lock()
Definition: mutex.cpp:136
fc::async
auto async(Functor &&f, const char *desc FC_TASK_NAME_DEFAULT_ARG, priority prio=priority()) -> fc::future< decltype(f())>
Definition: thread.hpp:227