Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
5be1c625
Commit
5be1c625
authored
Nov 22, 2014
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MinGW: Eliminate dependency on winpthread.
kernel32.dll and msvcrt.dll are the only remaining dependencies.
parent
1745aded
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
228 additions
and
75 deletions
+228
-75
Makefile.am
c++/Makefile.am
+5
-10
configure.ac
c++/configure.ac
+16
-1
acx_pthread.m4
c++/m4/acx_pthread.m4
+42
-3
mutex-test.c++
c++/src/kj/mutex-test.c++
+12
-1
mutex.c++
c++/src/kj/mutex.c++
+85
-41
mutex.h
c++/src/kj/mutex.h
+15
-19
thread.c++
c++/src/kj/thread.c++
+41
-0
thread.h
c++/src/kj/thread.h
+10
-0
threadlocal-pthread-test.c++
c++/src/kj/threadlocal-pthread-test.c++
+2
-0
No files found.
c++/Makefile.am
View file @
5be1c625
...
...
@@ -195,8 +195,7 @@ else
lib_LTLIBRARIES
=
libkj.la libkj-async.la libcapnp.la libcapnp-rpc.la libcapnpc.la
endif
# -lpthread is here to work around https://bugzilla.redhat.com/show_bug.cgi?id=661333
libkj_la_LIBADD
=
$(PTHREAD_LIBS)
-lpthread
libkj_la_LIBADD
=
$(PTHREAD_LIBS)
libkj_la_LDFLAGS
=
-release
$(VERSION)
-no-undefined
libkj_la_SOURCES
=
\
src/kj/common.c++
\
...
...
@@ -216,8 +215,7 @@ libkj_la_SOURCES= \
src/kj/parse/char.c++
if
!LITE_MODE
# -lpthread is here to work around https://bugzilla.redhat.com/show_bug.cgi?id=661333
libkj_async_la_LIBADD
=
libkj.la
$(PTHREAD_LIBS)
-lpthread
libkj_async_la_LIBADD
=
libkj.la
$(PTHREAD_LIBS)
libkj_async_la_LDFLAGS
=
-release
$(VERSION)
-no-undefined
libkj_async_la_SOURCES
=
\
src/kj/async.c++
\
...
...
@@ -233,8 +231,7 @@ heavy_sources = \
src/capnp/stringify.c++
endif
!LITE_MODE
# -lpthread is here to work around https://bugzilla.redhat.com/show_bug.cgi?id=661333
libcapnp_la_LIBADD
=
libkj.la
$(PTHREAD_LIBS)
-lpthread
libcapnp_la_LIBADD
=
libkj.la
$(PTHREAD_LIBS)
libcapnp_la_LDFLAGS
=
-release
$(VERSION)
-no-undefined
libcapnp_la_SOURCES
=
\
src/capnp/c++.capnp.c++
\
...
...
@@ -252,8 +249,7 @@ libcapnp_la_SOURCES= \
if
!LITE_MODE
# -lpthread is here to work around https://bugzilla.redhat.com/show_bug.cgi?id=661333
libcapnp_rpc_la_LIBADD
=
libcapnp.la libkj-async.la libkj.la
$(PTHREAD_LIBS)
-lpthread
libcapnp_rpc_la_LIBADD
=
libcapnp.la libkj-async.la libkj.la
$(PTHREAD_LIBS)
libcapnp_rpc_la_LDFLAGS
=
-release
$(VERSION)
-no-undefined
libcapnp_rpc_la_SOURCES
=
\
src/capnp/serialize-async.c++
\
...
...
@@ -266,8 +262,7 @@ libcapnp_rpc_la_SOURCES= \
src/capnp/persistent.capnp.c++
\
src/capnp/ez-rpc.c++
# -lpthread is here to work around https://bugzilla.redhat.com/show_bug.cgi?id=661333
libcapnpc_la_LIBADD
=
libcapnp.la libkj.la
$(PTHREAD_LIBS)
-lpthread
libcapnpc_la_LIBADD
=
libcapnp.la libkj.la
$(PTHREAD_LIBS)
libcapnpc_la_LDFLAGS
=
-release
$(VERSION)
-no-undefined
libcapnpc_la_SOURCES
=
\
src/capnp/compiler/md5.h
\
...
...
c++/configure.ac
View file @
5be1c625
...
...
@@ -51,7 +51,22 @@ AC_PROG_CC
AC_PROG_CXX
AC_LANG([C++])
AX_CXX_COMPILE_STDCXX_11
ACX_PTHREAD
AS_CASE("${host_os}", *mingw*, [
# We don't use pthreads on MinGW.
PTHREAD_CFLAGS="-mthreads"
PTHREAD_LIBS=""
PTHREAD_CC=""
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Disable pthreads when configuring gtest.
ac_configure_args="$ac_configure_args --without-pthreads"
], *, [
ACX_PTHREAD
])
LT_INIT
AS_IF([test "$external_capnp" != "no"], [
...
...
c++/m4/acx_pthread.m4
View file @
5be1c625
...
...
@@ -343,13 +343,52 @@ if test "x$acx_pthread_ok" = xyes; then
acx_pthread_ok=no
fi
CFLAGS="$save_CFLAGS"
LIBS="$save_LIBS"
CC="$save_CC"
CFLAGS="$save_CFLAGS"
LIBS="$save_LIBS"
CC="$save_CC"
else
PTHREAD_CC="$CC"
fi
if test "x$acx_pthread_ok" = xyes; then
# One more check: If we chose to use a compiler flag like -pthread but it is combined with
# -nostdlib then the compiler won't implicitly link against libpthread. This can happen
# in particular when using some versions of libtool on some distros. See:
# https://bugzilla.redhat.com/show_bug.cgi?id=661333
save_CFLAGS="$CFLAGS"
save_LIBS="$LIBS"
save_CC="$CC"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
LIBS="-nostdlib $PTHREAD_LIBS $LIBS -lc"
CC="$PTHREAD_CC"
AC_MSG_CHECKING([whether pthread flag is sufficient with -nostdlib])
AC_TRY_LINK([#include <pthread.h>],
[pthread_t th; pthread_join(th, 0);
pthread_attr_init(0); pthread_cleanup_push(0, 0);
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
[AC_MSG_RESULT([yes])], [
AC_MSG_RESULT([no])
AC_MSG_CHECKING([whether adding -lpthread fixes that])
LIBS="-nostdlib $PTHREAD_LIBS -lpthread $save_LIBS -lc"
AC_TRY_LINK([#include <pthread.h>],
[pthread_t th; pthread_join(th, 0);
pthread_attr_init(0); pthread_cleanup_push(0, 0);
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
[
AC_MSG_RESULT([yes])
PTHREAD_LIBS="$PTHREAD_LIBS -lpthread"
], [AC_MSG_RESULT([no])])
])
CFLAGS="$save_CFLAGS"
LIBS="$save_LIBS"
CC="$save_CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
...
...
c++/src/kj/mutex-test.c++
View file @
5be1c625
...
...
@@ -22,10 +22,15 @@
#include "mutex.h"
#include "debug.h"
#include "thread.h"
#include <pthread.h>
#include <unistd.h>
#include <gtest/gtest.h>
#if _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
namespace
kj
{
namespace
{
...
...
@@ -114,8 +119,10 @@ TEST(Mutex, MutexGuarded) {
EXPECT_EQ
(
321u
,
*
value
.
lockExclusive
());
#if !_WIN32 // Not checked on win32.
EXPECT_DEBUG_ANY_THROW
(
value
.
getAlreadyLockedExclusive
());
EXPECT_DEBUG_ANY_THROW
(
value
.
getAlreadyLockedShared
());
#endif
EXPECT_EQ
(
321u
,
value
.
getWithoutLock
());
}
...
...
@@ -133,7 +140,11 @@ TEST(Mutex, Lazy) {
// Spin until the initializer has been entered in the thread.
while
(
!
__atomic_load_n
(
&
initStarted
,
__ATOMIC_RELAXED
))
{
#if _WIN32
Sleep
(
0
);
#else
sched_yield
();
#endif
}
EXPECT_EQ
(
123u
,
lazy
.
get
([](
SpaceFor
<
uint
>&
space
)
{
return
space
.
construct
(
456
);
}));
...
...
c++/src/kj/mutex.c++
View file @
5be1c625
...
...
@@ -19,6 +19,12 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if _WIN32
#define WIN32_LEAN_AND_MEAN 1 // lolz
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#endif
#include "mutex.h"
#include "debug.h"
...
...
@@ -27,6 +33,8 @@
#include <sys/syscall.h>
#include <linux/futex.h>
#include <limits.h>
#elif _WIN32
#include <windows.h>
#endif
namespace
kj
{
...
...
@@ -159,7 +167,7 @@ startOver:
}
}
else
{
for
(;;)
{
if
(
state
==
INITIALIZED
||
state
==
DISABLED
)
{
if
(
state
==
INITIALIZED
)
{
break
;
}
else
if
(
state
==
INITIALIZING
)
{
// Initialization is taking place in another thread. Indicate that we're waiting.
...
...
@@ -189,47 +197,90 @@ void Once::reset() {
uint
state
=
INITIALIZED
;
if
(
!
__atomic_compare_exchange_n
(
&
futex
,
&
state
,
UNINITIALIZED
,
false
,
__ATOMIC_RELEASE
,
__ATOMIC_RELAXED
))
{
KJ_
REQUIRE
(
state
==
DISABLED
,
"reset() called while not initialized."
);
KJ_
FAIL_REQUIRE
(
"reset() called while not initialized."
);
}
}
void
Once
::
disable
()
noexcept
{
uint
state
=
__atomic_load_n
(
&
futex
,
__ATOMIC_ACQUIRE
);
for
(;;)
{
switch
(
state
)
{
case
DISABLED
:
default
:
return
;
case
UNINITIALIZED
:
case
INITIALIZED
:
// Try to transition the state to DISABLED.
if
(
!
__atomic_compare_exchange_n
(
&
futex
,
&
state
,
DISABLED
,
true
,
__ATOMIC_RELAXED
,
__ATOMIC_RELAXED
))
{
// State changed, retry.
continue
;
}
// Success.
return
;
#elif _WIN32
// =======================================================================================
// Win32 implementation
case
INITIALIZING
:
// Initialization is taking place in another thread. Indicate that we're waiting.
if
(
!
__atomic_compare_exchange_n
(
&
futex
,
&
state
,
INITIALIZING_WITH_WAITERS
,
true
,
__ATOMIC_ACQUIRE
,
__ATOMIC_ACQUIRE
))
{
// State changed, retry.
continue
;
}
// no break
#define coercedSrwLock (*reinterpret_cast<SRWLOCK*>(&srwLock))
#define coercedInitOnce (*reinterpret_cast<INIT_ONCE*>(&initOnce))
case
INITIALIZING_WITH_WAITERS
:
// Wait for initialization.
syscall
(
SYS_futex
,
&
futex
,
FUTEX_WAIT_PRIVATE
,
INITIALIZING_WITH_WAITERS
,
NULL
,
NULL
,
0
);
state
=
__atomic_load_n
(
&
futex
,
__ATOMIC_ACQUIRE
);
continue
;
Mutex
::
Mutex
()
{
static_assert
(
sizeof
(
SRWLOCK
)
==
sizeof
(
srwLock
),
"SRWLOCK is not a pointer?"
);
InitializeSRWLock
(
&
coercedSrwLock
);
}
Mutex
::~
Mutex
()
{}
void
Mutex
::
lock
(
Exclusivity
exclusivity
)
{
switch
(
exclusivity
)
{
case
EXCLUSIVE
:
AcquireSRWLockExclusive
(
&
coercedSrwLock
);
break
;
case
SHARED
:
AcquireSRWLockShared
(
&
coercedSrwLock
);
break
;
}
}
void
Mutex
::
unlock
(
Exclusivity
exclusivity
)
{
switch
(
exclusivity
)
{
case
EXCLUSIVE
:
ReleaseSRWLockExclusive
(
&
coercedSrwLock
);
break
;
case
SHARED
:
ReleaseSRWLockShared
(
&
coercedSrwLock
);
break
;
}
}
void
Mutex
::
assertLockedByCaller
(
Exclusivity
exclusivity
)
{
// We could use TryAcquireSRWLock*() here like we do with the pthread version. However, as of
// this writing, my version of Wine (1.6.2) doesn't implement these functions and will abort if
// they are called. Since we were only going to use them as a hacky way to check if the lock is
// held for debug purposes anyway, we just don't bother.
}
static
BOOL
nullInitializer
(
PINIT_ONCE
initOnce
,
PVOID
parameter
,
PVOID
*
context
)
{
return
true
;
}
Once
::
Once
(
bool
startInitialized
)
{
static_assert
(
sizeof
(
INIT_ONCE
)
==
sizeof
(
initOnce
),
"INIT_ONCE is not a pointer?"
);
InitOnceInitialize
(
&
coercedInitOnce
);
if
(
startInitialized
)
{
InitOnceExecuteOnce
(
&
coercedInitOnce
,
&
nullInitializer
,
nullptr
,
nullptr
);
}
}
Once
::~
Once
()
{}
void
Once
::
runOnce
(
Initializer
&
init
)
{
BOOL
needInit
;
while
(
!
InitOnceBeginInitialize
(
&
coercedInitOnce
,
0
,
&
needInit
,
nullptr
))
{
// Init was occurring in another thread, but then failed with an exception. Retry.
}
if
(
needInit
)
{
{
KJ_ON_SCOPE_FAILURE
(
InitOnceComplete
(
&
coercedInitOnce
,
INIT_ONCE_INIT_FAILED
,
nullptr
));
init
.
run
();
}
KJ_ASSERT
(
InitOnceComplete
(
&
coercedInitOnce
,
0
,
nullptr
));
}
}
bool
Once
::
isInitialized
()
noexcept
{
BOOL
junk
;
return
InitOnceBeginInitialize
(
&
coercedInitOnce
,
INIT_ONCE_CHECK_ONLY
,
&
junk
,
nullptr
);
}
void
Once
::
reset
()
{
InitOnceInitialize
(
&
coercedInitOnce
);
}
#else
// =======================================================================================
// Generic pthreads-based implementation
...
...
@@ -316,17 +367,10 @@ void Once::reset() {
State
oldState
=
INITIALIZED
;
if
(
!
__atomic_compare_exchange_n
(
&
state
,
&
oldState
,
UNINITIALIZED
,
false
,
__ATOMIC_RELEASE
,
__ATOMIC_RELAXED
))
{
KJ_
REQUIRE
(
oldState
==
DISABLED
,
"reset() called while not initialized."
);
KJ_
FAIL_REQUIRE
(
"reset() called while not initialized."
);
}
}
void
Once
::
disable
()
noexcept
{
KJ_PTHREAD_CALL
(
pthread_mutex_lock
(
&
mutex
));
KJ_DEFER
(
KJ_PTHREAD_CALL
(
pthread_mutex_unlock
(
&
mutex
)));
__atomic_store_n
(
&
state
,
DISABLED
,
__ATOMIC_RELAXED
);
}
#endif
}
// namespace _ (private)
...
...
c++/src/kj/mutex.h
View file @
5be1c625
...
...
@@ -23,12 +23,13 @@
#define KJ_MUTEX_H_
#include "memory.h"
#include <inttypes.h>
#if __linux__ && !defined(KJ_USE_FUTEX)
#define KJ_USE_FUTEX 1
#endif
#if !KJ_USE_FUTEX
#if !KJ_USE_FUTEX
&& !_WIN32
// On Linux we use futex. On other platforms we wrap pthreads.
// TODO(someday): Write efficient low-level locking primitives for other platforms.
#include <pthread.h>
...
...
@@ -75,6 +76,9 @@ private:
static
constexpr
uint
EXCLUSIVE_REQUESTED
=
1u
<<
30
;
static
constexpr
uint
SHARED_COUNT_MASK
=
EXCLUSIVE_REQUESTED
-
1
;
#elif _WIN32
uintptr_t
srwLock
;
// Actually an SRWLOCK, but don't want to #include <windows.h> in header.
#else
mutable
pthread_rwlock_t
mutex
;
#endif
...
...
@@ -100,6 +104,10 @@ public:
void
runOnce
(
Initializer
&
init
);
#if _WIN32 // TODO(perf): Can we make this inline on win32 somehow?
bool
isInitialized
()
noexcept
;
#else
inline
bool
isInitialized
()
noexcept
{
// Fast path check to see if runOnce() would simply return immediately.
#if KJ_USE_FUTEX
...
...
@@ -108,26 +116,13 @@ public:
return
__atomic_load_n
(
&
state
,
__ATOMIC_ACQUIRE
)
==
INITIALIZED
;
#endif
}
#endif
void
reset
();
// Returns the state from initialized to uninitialized. It is an error to call this when
// not already initialized, or when runOnce() or isInitialized() might be called concurrently in
// another thread.
void
disable
()
noexcept
;
// Prevent future calls to runOnce() and reset() from having any effect, and make isInitialized()
// return false forever. If an initializer is currently running, block until it completes.
bool
isDisabled
()
noexcept
{
// Returns true if `disable()` has been called.
#if KJ_USE_FUTEX
return
__atomic_load_n
(
&
futex
,
__ATOMIC_ACQUIRE
)
==
DISABLED
;
#else
return
__atomic_load_n
(
&
state
,
__ATOMIC_ACQUIRE
)
==
DISABLED
;
#endif
}
private
:
#if KJ_USE_FUTEX
uint
futex
;
...
...
@@ -136,15 +131,16 @@ private:
UNINITIALIZED
,
INITIALIZING
,
INITIALIZING_WITH_WAITERS
,
INITIALIZED
,
DISABLED
INITIALIZED
};
#elif _WIN32
uintptr_t
initOnce
;
// Actually an INIT_ONCE, but don't want to #include <windows.h> in header.
#else
enum
State
{
UNINITIALIZED
,
INITIALIZED
,
DISABLED
INITIALIZED
};
State
state
;
pthread_mutex_t
mutex
;
...
...
c++/src/kj/thread.c++
View file @
5be1c625
...
...
@@ -21,11 +21,50 @@
#include "thread.h"
#include "debug.h"
#if _WIN32
#include <windows.h>
#else
#include <pthread.h>
#include <signal.h>
#endif
namespace
kj
{
#if _WIN32
Thread
::
Thread
(
Function
<
void
()
>
func
)
:
func
(
kj
::
mv
(
func
))
{
threadHandle
=
CreateThread
(
nullptr
,
0
,
&
runThread
,
this
,
0
,
nullptr
);
KJ_ASSERT
(
threadHandle
!=
nullptr
,
"CreateThread failed."
);
}
Thread
::~
Thread
()
noexcept
(
false
)
{
if
(
!
detached
)
{
KJ_ASSERT
(
WaitForSingleObject
(
threadHandle
,
INFINITE
)
!=
WAIT_FAILED
);
KJ_IF_MAYBE
(
e
,
exception
)
{
kj
::
throwRecoverableException
(
kj
::
mv
(
*
e
));
}
}
}
void
Thread
::
detach
()
{
KJ_ASSERT
(
CloseHandle
(
threadHandle
));
detached
=
true
;
}
DWORD
Thread
::
runThread
(
void
*
ptr
)
{
Thread
*
thread
=
reinterpret_cast
<
Thread
*>
(
ptr
);
KJ_IF_MAYBE
(
exception
,
kj
::
runCatchingExceptions
([
&
]()
{
thread
->
func
();
}))
{
thread
->
exception
=
kj
::
mv
(
*
exception
);
}
return
0
;
}
#else // _WIN32
Thread
::
Thread
(
Function
<
void
()
>
func
)
:
func
(
kj
::
mv
(
func
))
{
static_assert
(
sizeof
(
threadId
)
>=
sizeof
(
pthread_t
),
"pthread_t is larger than a long long on your platform. Please port."
);
...
...
@@ -75,4 +114,6 @@ void* Thread::runThread(void* ptr) {
return
nullptr
;
}
#endif // _WIN32, else
}
// namespace kj
c++/src/kj/thread.h
View file @
5be1c625
...
...
@@ -38,19 +38,29 @@ public:
~
Thread
()
noexcept
(
false
);
#if !_WIN32
void
sendSignal
(
int
signo
);
// Send a Unix signal to the given thread, using pthread_kill or an equivalent.
#endif
void
detach
();
// Don't join the thread in ~Thread().
private
:
Function
<
void
()
>
func
;
#if _WIN32
void
*
threadHandle
;
#else
unsigned
long
long
threadId
;
// actually pthread_t
#endif
kj
::
Maybe
<
kj
::
Exception
>
exception
;
bool
detached
=
false
;
#if _WIN32
static
unsigned
long
runThread
(
void
*
ptr
);
#else
static
void
*
runThread
(
void
*
ptr
);
#endif
};
}
// namespace kj
...
...
c++/src/kj/threadlocal-pthread-test.c++
View file @
5be1c625
...
...
@@ -19,5 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if !_WIN32
#define KJ_USE_PTHREAD_TLS 1
#include "threadlocal-test.c++"
#endif
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