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
Nov 04, 2015
by
Christian Kern
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Spinlock implementation and basic tests.
parent
be3ead1e
Hide 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
...
...
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
;
}
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
);
}
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
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
...
...
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
...
...
base_cpp/src/mutex.cc
View file @
44cc89cd
...
...
@@ -62,6 +62,3 @@ RecursiveMutex::RecursiveMutex() : MutexBase(EMBB_MUTEX_RECURSIVE) {
}
// namespace base
}
// namespace embb
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
);
...
...
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
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
...
...
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