Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
FORMUS3IC_LAS3
/
embb
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
44cc89cd
authored
9 years ago
by
Christian Kern
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Spinlock implementation and basic tests.
parent
be3ead1e
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
473 additions
and
11 deletions
+473
-11
base_c/include/embb/base/c/mutex.h
+89
-0
base_c/src/mutex.c
+54
-0
base_c/test/main.cc
+2
-1
base_c/test/mutex_test.cc
+71
-0
base_c/test/mutex_test.h
+57
-0
base_cpp/include/embb/base/mutex.h
+103
-6
base_cpp/src/mutex.cc
+0
-3
base_cpp/test/main.cc
+3
-0
base_cpp/test/mutex_test.cc
+51
-1
base_cpp/test/mutex_test.h
+43
-0
No files found.
base_c/include/embb/base/c/mutex.h
View file @
44cc89cd
...
...
@@ -46,12 +46,25 @@ extern "C" {
#include <embb/base/c/internal/platform.h>
#include <embb/base/c/errors.h>
#include <embb/base/c/atomic.h>
#ifdef DOXYGEN
/**
* Opaque type representing a mutex.
*/
typedef
opaque_type
embb_mutex_t
;
/**
* Opaque type representing a spinlock.
*/
typedef
opaque_type
embb_spinlock_t
;
#else
/**
* Spinlock type, treat as opaque.
*/
typedef
struct
{
embb_atomic_int
atomic_spin_variable_
;
}
embb_spinlock_t
;
#endif
/* DOXYGEN */
/**
...
...
@@ -147,6 +160,82 @@ void embb_mutex_destroy(
/**< [IN/OUT] Pointer to mutex */
);
/**
* Initializes a spinlock
*
* \post \c spinlock is initialized
* \return EMBB_SUCCESS if spinlock could be initialized \n
* EMBB_ERROR otherwise
* \memory (Potentially) allocates dynamic memory
* \notthreadsafe
* \see embb_spinlock_destroy()
*/
int
embb_spin_init
(
embb_spinlock_t
*
spinlock
/**< [OUT] Pointer to spinlock */
);
/**
* Spins until the spinlock can be locked and locks it.
*
* \pre \c spinlock is initialized \n
* \post If successful, \c spinlock is locked.
* \return EMBB_SUCCESS if spinlock could be locked.
* \threadsafe
* \see embb_spinlock_try_lock(), embb_mutex_unlock()
*/
int
embb_spin_lock
(
embb_spinlock_t
*
spinlock
/**< [IN/OUT] Pointer to spinlock */
);
/**
* Tries to lock the spinlock and returns if not successful.
*
* \pre \c spinlock is initialized
* \post If successful, \c spinlock is locked
*
* \return EMBB_SUCCESS if spinlock could be locked \n
* EMBB_BUSY if spinlock could not be locked \n
* \threadsafe
* \see embb_spin_lock(), embb_spin_unlock()
*/
int
embb_spin_try_lock
(
embb_spinlock_t
*
spinlock
,
/**< [IN/OUT] Pointer to spinlock */
unsigned
int
max_number_spins
/**< [IN] Number of attempts the locking operation is repeated if
* unsuccessful */
);
/**
* Unlocks a locked spinlock.
*
* \pre \c spinlock has been locked by the current thread.
* \post If successful, \c spinlock is unlocked.
* \return EMBB_SUCCESS if the operation was successful \n
* EMBB_ERROR otherwise
* \threadsafe
* \see embb_spin_lock(), embb_spin_try_lock()
*/
int
embb_spin_unlock
(
embb_spinlock_t
*
spinlock
/**< [IN/OUT] Pointer to spinlock */
);
/**
* Destroys a spinlock and frees its resources.
*
* \pre \c spinlock has been initialized
* \post \c spinlock is uninitialized
* \notthreadsafe
* \see embb_spin_init()
*/
void
embb_spin_destroy
(
embb_spinlock_t
*
spinlock
/**< [IN/OUT] Pointer to spinlock */
);
#ifdef __cplusplus
}
/* Close extern "C" { */
#endif
...
...
This diff is collapsed.
Click to expand it.
base_c/src/mutex.c
View file @
44cc89cd
...
...
@@ -115,3 +115,57 @@ void embb_mutex_destroy(embb_mutex_t* mutex) {
}
#endif
/* EMBB_PLATFORM_THREADING_POSIXTHREADS */
int
embb_spin_init
(
embb_spinlock_t
*
spinlock
)
{
// for now, just assign the internal struct value... in the future,
// we will have an atomic init function.
spinlock
->
atomic_spin_variable_
.
internal_variable
=
0
;
}
int
embb_spin_lock
(
embb_spinlock_t
*
spinlock
)
{
int
expected
=
0
;
// try to swap the
while
(
0
==
embb_atomic_compare_and_swap_int
(
&
spinlock
->
atomic_spin_variable_
,
&
expected
,
1
))
{
// mtapi has a debug variable, counting spins... think about that...
// embb_atomic_fetch_and_add_int(&embb_mtapi_spinlock_spins, 1);
// reset expected, as CAS might change it...
expected
=
0
;
}
return
EMBB_SUCCESS
;
}
int
embb_spin_try_lock
(
embb_spinlock_t
*
spinlock
,
unsigned
int
max_number_spins
)
{
int
expected
=
0
;
unsigned
int
spin_count
=
max_number_spins
;
while
(
0
==
embb_atomic_compare_and_swap_int
(
&
spinlock
->
atomic_spin_variable_
,
&
expected
,
1
))
{
// mtapi has a debug variable, counting spins... think about that...
// embb_atomic_fetch_and_add_int(&embb_mtapi_spinlock_spins, 1);
spin_count
--
;
if
(
0
==
spin_count
)
{
return
EMBB_BUSY
;
}
expected
=
0
;
}
return
EMBB_SUCCESS
;
}
int
embb_spin_unlock
(
embb_spinlock_t
*
spinlock
)
{
int
expected
=
1
;
return
embb_atomic_compare_and_swap_int
(
&
spinlock
->
atomic_spin_variable_
,
&
expected
,
0
)
?
EMBB_SUCCESS
:
EMBB_ERROR
;
}
void
embb_spin_destroy
(
embb_spinlock_t
*
spinlock
)
{
// for now, doing nothing here... in future, will call the respective
// destroy function for atomics...
return
EMBB_SUCCESS
;
}
This diff is collapsed.
Click to expand it.
base_c/test/main.cc
View file @
44cc89cd
...
...
@@ -46,6 +46,7 @@ using embb::base::test::DurationTest;
using
embb
::
base
::
test
::
TimeTest
;
using
embb
::
base
::
test
::
CounterTest
;
using
embb
::
base
::
test
::
MutexTest
;
using
embb
::
base
::
test
::
SpinLockTest
;
using
embb
::
base
::
test
::
ThreadIndexTest
;
using
embb
::
base
::
test
::
CoreSetTest
;
using
embb
::
base
::
test
::
ConditionVarTest
;
...
...
@@ -63,11 +64,11 @@ PT_MAIN("Base C") {
PT_RUN
(
TimeTest
);
PT_RUN
(
CounterTest
);
PT_RUN
(
MutexTest
);
PT_RUN
(
SpinLockTest
);
PT_RUN
(
ThreadIndexTest
);
PT_RUN
(
CoreSetTest
);
PT_RUN
(
ConditionVarTest
);
PT_RUN
(
ThreadTest
);
PT_RUN
(
ThreadSpecificStorageTest
);
PT_EXPECT
(
embb_get_bytes_allocated
()
==
0
);
}
This diff is collapsed.
Click to expand it.
base_c/test/mutex_test.cc
View file @
44cc89cd
...
...
@@ -76,6 +76,77 @@ void MutexTest::TestRecursiveMutex() {
embb_mutex_destroy
(
&
mutex
);
}
SpinLockTest
::
SpinLockTest
()
:
counter_
(
0
),
number_threads_
(
partest
::
TestSuite
::
GetDefaultNumThreads
()),
number_iterations_
(
partest
::
TestSuite
::
GetDefaultNumIterations
()),
counter_iterations_
(
10000
)
{
CreateUnit
(
"Protected counter using Lock"
)
.
Pre
(
&
SpinLockTest
::
PreSpinLockInc
,
this
)
.
Add
(
&
SpinLockTest
::
TestSpinLockIncUseLock
,
this
,
number_threads_
,
number_iterations_
)
.
Post
(
&
SpinLockTest
::
PostSpinLockInc
,
this
);
CreateUnit
(
"Protected counter using TryLock"
)
.
Pre
(
&
SpinLockTest
::
PreSpinLockInc
,
this
)
.
Add
(
&
SpinLockTest
::
TestSpinLockIncUseTryLock
,
this
,
number_threads_
,
number_iterations_
)
.
Post
(
&
SpinLockTest
::
PostSpinLockInc
,
this
);
CreateUnit
(
"Test spinning (too many spins), single thread"
)
.
Add
(
&
SpinLockTest
::
TestSpinLockTooManySpins
,
this
,
// one thread
1
,
// one iteration
1
);
}
void
SpinLockTest
::
TestSpinLockTooManySpins
()
{
embb_spin_init
(
&
spinlock_
);
embb_spin_lock
(
&
spinlock_
);
int
return_code
=
embb_spin_try_lock
(
&
spinlock_
,
100
);
PT_ASSERT
(
return_code
==
EMBB_BUSY
);
embb_spin_unlock
(
&
spinlock_
);
return_code
=
embb_spin_try_lock
(
&
spinlock_
,
100
);
PT_ASSERT
(
return_code
==
EMBB_SUCCESS
);
embb_spin_unlock
(
&
spinlock_
);
embb_spin_destroy
(
&
spinlock_
);
}
void
SpinLockTest
::
PreSpinLockInc
()
{
embb_spin_init
(
&
spinlock_
);
}
void
SpinLockTest
::
TestSpinLockIncUseLock
()
{
for
(
unsigned
int
i
=
0
;
i
!=
counter_iterations_
;
++
i
){
embb_spin_lock
(
&
spinlock_
);
counter_
++
;
embb_spin_unlock
(
&
spinlock_
);
}
}
void
SpinLockTest
::
TestSpinLockIncUseTryLock
()
{
for
(
unsigned
int
i
=
0
;
i
!=
counter_iterations_
;
++
i
){
while
(
embb_spin_try_lock
(
&
spinlock_
,
100
)
!=
EMBB_SUCCESS
)
{}
counter_
++
;
embb_spin_unlock
(
&
spinlock_
);
}
}
void
SpinLockTest
::
PostSpinLockInc
()
{
embb_spin_destroy
(
&
spinlock_
);
PT_EXPECT_EQ
(
counter_
,
number_iterations_
*
number_threads_
*
counter_iterations_
);
counter_
=
0
;
}
}
// namespace test
}
// namespace base
}
// namespace embb
This diff is collapsed.
Click to expand it.
base_c/test/mutex_test.h
View file @
44cc89cd
...
...
@@ -85,6 +85,63 @@ class MutexTest : public partest::TestCase {
size_t
number_iterations_
;
};
class
SpinLockTest
:
public
partest
::
TestCase
{
public
:
SpinLockTest
();
private
:
/**
* Check that the try lock fails, when lock is already set.
*/
void
TestSpinLockTooManySpins
();
/**
* Prepares TestMutexIncCpp.
*/
void
PreSpinLockInc
();
/**
* Tests mutex locking and unlocking to protect shared counter.
*/
void
TestSpinLockIncUseLock
();
/**
* Tests mutex locking and unlocking to protect shared counter using trylock.
*/
void
TestSpinLockIncUseTryLock
();
/**
* Checks and tears down TestMutexIncCpp.
*/
void
PostSpinLockInc
();
/**
* Shared counter to check effectiveness of mutex.
*/
size_t
counter_
;
/**
* Number of threads used to run tests.
*/
size_t
number_threads_
;
/**
* Number of times the test method is called by each thread.
*/
size_t
number_iterations_
;
/**
* Number of internal iterations, for incrementing the counter.
*/
size_t
counter_iterations_
;
/**
* The used spinlock
*/
embb_spinlock_t
spinlock_
;
};
}
// namespace test
}
// namespace base
}
// namespace embb
...
...
This diff is collapsed.
Click to expand it.
base_cpp/include/embb/base/mutex.h
View file @
44cc89cd
...
...
@@ -29,10 +29,10 @@
#include <embb/base/internal/platform.h>
#include <embb/base/exceptions.h>
#include <embb/base/c/mutex.h>
namespace
embb
{
namespace
base
{
/**
* \defgroup CPP_BASE_MUTEX Mutex and Lock
*
...
...
@@ -47,7 +47,6 @@ namespace base {
class
ConditionVariable
;
namespace
internal
{
/**
* Provides main functionality for mutexes.
*/
...
...
@@ -111,10 +110,111 @@ class MutexBase {
*/
friend
class
embb
::
base
::
ConditionVariable
;
};
}
// namespace internal
/**
* \defgroup CPP_BASE_SPINLOCK Spinlock
*
* Spinlock for thread synchronization.
*
* \ingroup CPP_BASE
*/
/**
* Spinlock
*
* \ingroup CPP_BASE_SPINLOCK
*/
class
Spinlock
{
public
:
/**
* Creates a spinlock which is in unlocked state.
*
* \notthreadsafe
*/
Spinlock
()
{
embb_spin_init
(
&
spinlock_
);
}
/**
* Destructs a spinlock.
*
* \notthreadsafe
*/
~
Spinlock
()
{
embb_spin_destroy
(
&
spinlock_
);
}
/**
* Waits until the spinlock can be locked and locks it.
*
* \pre The spinlock is not locked by the current thread.
* \post The spinlock is locked
* \threadsafe
* \see TryLock(), Unlock()
*/
void
Lock
()
{
int
status
=
embb_spin_lock
(
&
spinlock_
);
// Currently, embb_spin_lock will always return EMBB_SUCCESS. However,
// This might change.
if
(
status
!=
EMBB_SUCCESS
)
{
EMBB_THROW
(
ErrorException
,
"Error in embb_spin_lock"
);
}
}
/**
* Tries to lock the spinlock for \c number_spins times and returns.
*
* \pre The spinlock is not locked by the current thread.
* \post If successful, the spinlock is locked.
* \return \c true if the spinlock could be locked, otherwise \c false.
* \threadsafe
* \see Lock(), Unlock()
*/
bool
TryLock
(
unsigned
int
number_spins
=
1
)
{
int
status
=
embb_spin_try_lock
(
&
spinlock_
,
number_spins
);
if
(
status
==
EMBB_BUSY
){
return
false
;
}
else
if
(
status
!=
EMBB_SUCCESS
)
{
EMBB_THROW
(
ErrorException
,
"Error in embb_spin_try_lock"
);
}
return
true
;
}
/**
* Unlocks the spinlock.
*
* \pre The spinlock is locked by the current thread
* \post The spinlock is unlocked
* \threadsafe
* \see Lock(), TryLock()
*/
void
Unlock
()
{
int
status
=
embb_spin_unlock
(
&
spinlock_
);
if
(
status
!=
EMBB_SUCCESS
)
{
EMBB_THROW
(
ErrorException
,
"Error in embb_spin_unlock"
);
}
}
private
:
/**
* Disables copy construction and assignment.
*/
Spinlock
(
const
Spinlock
&
);
Spinlock
&
operator
=
(
const
Spinlock
&
);
/**
* Internal spinlock from base_c
*/
embb_spinlock_t
spinlock_
;
};
/**
* Non-recursive, exclusive mutex.
*
* Mutexes of this type cannot be locked recursively, that is, multiple times
...
...
@@ -182,7 +282,6 @@ class Mutex : public internal::MutexBase {
friend
class
ConditionVariable
;
};
/**
* Recursive, exclusive mutex.
*
...
...
@@ -246,7 +345,6 @@ class RecursiveMutex : public internal::MutexBase {
RecursiveMutex
&
operator
=
(
const
RecursiveMutex
&
);
};
/**
* Scoped lock (according to the RAII principle) using a mutex.
*
...
...
@@ -482,7 +580,6 @@ class UniqueLock {
*/
friend
class
embb
::
base
::
ConditionVariable
;
};
}
// namespace base
}
// namespace embb
...
...
This diff is collapsed.
Click to expand it.
base_cpp/src/mutex.cc
View file @
44cc89cd
...
...
@@ -62,6 +62,3 @@ RecursiveMutex::RecursiveMutex() : MutexBase(EMBB_MUTEX_RECURSIVE) {
}
// namespace base
}
// namespace embb
This diff is collapsed.
Click to expand it.
base_cpp/test/main.cc
View file @
44cc89cd
...
...
@@ -41,6 +41,7 @@ using embb::base::test::CoreSetTest;
using
embb
::
base
::
test
::
DurationTest
;
using
embb
::
base
::
test
::
ConditionVarTest
;
using
embb
::
base
::
test
::
MutexTest
;
using
embb
::
base
::
test
::
SpinLockTest
;
using
embb
::
base
::
test
::
ThreadSpecificStorageTest
;
using
embb
::
base
::
test
::
AtomicTest
;
using
embb
::
base
::
test
::
MemoryAllocationTest
;
...
...
@@ -50,10 +51,12 @@ PT_MAIN("Base C++") {
unsigned
int
max_threads
=
static_cast
<
unsigned
int
>
(
2
*
partest
::
TestSuite
::
GetDefaultNumThreads
());
embb_thread_set_max_count
(
max_threads
);
PT_RUN
(
CoreSetTest
);
PT_RUN
(
DurationTest
);
PT_RUN
(
ConditionVarTest
);
PT_RUN
(
MutexTest
);
PT_RUN
(
SpinLockTest
);
PT_RUN
(
ThreadSpecificStorageTest
);
PT_RUN
(
AtomicTest
);
PT_RUN
(
MemoryAllocationTest
);
...
...
This diff is collapsed.
Click to expand it.
base_cpp/test/mutex_test.cc
View file @
44cc89cd
...
...
@@ -32,7 +32,6 @@
namespace
embb
{
namespace
base
{
namespace
test
{
MutexTest
::
MutexTest
()
:
mutex_
(),
counter_
(
0
),
number_threads_
(
partest
::
TestSuite
::
GetDefaultNumThreads
()),
number_iterations_
(
partest
::
TestSuite
::
GetDefaultNumIterations
())
{
...
...
@@ -209,6 +208,57 @@ void MutexTest::TestUniqueLock() {
}
}
SpinLockTest
::
SpinLockTest
()
:
spinlock_
(),
counter_
(
0
),
number_threads_
(
partest
::
TestSuite
::
GetDefaultNumThreads
()),
number_iterations_
(
partest
::
TestSuite
::
GetDefaultNumIterations
()),
counter_iterations_
(
10000
)
{
CreateUnit
(
"Spinlock protected counter (using Lock)"
)
.
Add
(
&
SpinLockTest
::
TestSpinlockCountLock
,
this
,
number_threads_
,
number_iterations_
)
.
Post
(
&
SpinLockTest
::
PostSpinlockCount
,
this
);
CreateUnit
(
"Spinlock protected counter (using Trylock)"
)
.
Add
(
&
SpinLockTest
::
TestSpinlockCountLockTryLock
,
this
,
number_threads_
,
number_iterations_
)
.
Post
(
&
SpinLockTest
::
PostSpinlockCount
,
this
);
CreateUnit
(
"Test spinning (too many spins), single thread"
)
.
Add
(
&
SpinLockTest
::
TestSpinLockTooManySpins
,
this
,
1
,
1
);
}
void
SpinLockTest
::
TestSpinlockCountLock
()
{
for
(
unsigned
int
i
=
0
;
i
!=
counter_iterations_
;
++
i
){
spinlock_
.
Lock
();
counter_
++
;
spinlock_
.
Unlock
();
}
}
void
SpinLockTest
::
TestSpinlockCountLockTryLock
()
{
for
(
unsigned
int
i
=
0
;
i
!=
counter_iterations_
;
++
i
){
while
(
!
spinlock_
.
TryLock
())
{}
counter_
++
;
spinlock_
.
Unlock
();
}
}
void
SpinLockTest
::
PostSpinlockCount
()
{
PT_EXPECT_EQ
(
counter_
,
number_iterations_
*
number_threads_
*
counter_iterations_
);
counter_
=
0
;
}
void
SpinLockTest
::
TestSpinLockTooManySpins
()
{
Spinlock
lock
;
lock
.
Lock
();
bool
success
=
lock
.
TryLock
(
100
);
PT_ASSERT
(
!
success
);
lock
.
Unlock
();
success
=
lock
.
TryLock
(
100
);
PT_ASSERT
(
success
);
}
}
// namespace test
}
// namespace base
}
// namespace embb
This diff is collapsed.
Click to expand it.
base_cpp/test/mutex_test.h
View file @
44cc89cd
...
...
@@ -89,6 +89,49 @@ class MutexTest : public partest::TestCase {
size_t
number_iterations_
;
};
class
SpinLockTest
:
public
partest
::
TestCase
{
public
:
SpinLockTest
();
private
:
/**
* Uses Spinlock to realize multi-threaded counting.
*/
void
TestSpinlockCountLock
();
void
TestSpinlockCountLockTryLock
();
void
PostSpinlockCount
();
/**
* Test that TryLock returns false, if lock is already locked.
*/
void
TestSpinLockTooManySpins
();
/**
* Spinlock for tests
*/
Spinlock
spinlock_
;
/**
* Shared counter to check effectiveness of mutex.
*/
size_t
counter_
;
/**
* Number of threads used to run tests.
*/
size_t
number_threads_
;
/**
* Number of times the test method is called by each thread.
*/
size_t
number_iterations_
;
/**
* Number of internal iterations, for incrementing the counter.
*/
size_t
counter_iterations_
;
};
}
// namespace test
}
// namespace base
}
// namespace embb
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment