Commit 3cbfac42 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Use signals to get the registers of other threads

parent fee15977
......@@ -293,7 +293,7 @@ run_unittests:: run_$1_unittests
endef
override GDB_CMDS ?=
override GDB_CMDS := --ex "set confirm off" --ex run --ex "bt 20" $(GDB_CMDS)
override GDB_CMDS := --ex "set confirm off" --ex "handle SIGUSR2 pass nostop noprint" --ex run --ex "bt 20" $(GDB_CMDS)
BR ?=
ARGS ?=
ifneq ($(BR),)
......
......@@ -14,18 +14,172 @@
#include "core/threading.h"
#include <cassert>
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <pthread.h>
#include <stdint.h>
#include <err.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "core/common.h"
#include "core/options.h"
namespace pyston {
namespace threading {
// Linux specific: TODO should be in a plat/linux/ directory?
pid_t gettid() {
pid_t tid = syscall(SYS_gettid);
assert(tid > 0);
return tid;
}
int tgkill(int tgid, int tid, int sig) {
return syscall(SYS_tgkill, tgid, tid, sig);
}
class LockedRegion {
private:
pthread_mutex_t* mutex;
public:
LockedRegion(pthread_mutex_t* mutex) : mutex(mutex) { pthread_mutex_lock(mutex); }
~LockedRegion() { pthread_mutex_unlock(mutex); }
};
// Certain thread examination functions won't be valid for a brief
// period while a thread is starting up.
// To handle this, track the number of threads in an uninitialized state,
// and wait until they start up.
int num_starting_threads(0);
struct ThreadStartArgs {
void* (*start_func)(Box*, Box*, Box*);
Box* arg1, *arg2, *arg3;
};
static pthread_mutex_t threading_lock = PTHREAD_MUTEX_INITIALIZER;
static std::vector<pid_t> current_threads;
static std::atomic<int> signals_waiting(0);
static std::vector<ThreadState> thread_states;
std::vector<ThreadState> getAllThreadStates() {
// TODO need to prevent new threads from starting,
// though I suppose that will have been taken care of
// by the caller of this function.
LockedRegion _lock(&threading_lock);
while (true) {
// TODO shouldn't busy-wait:
if (num_starting_threads) {
pthread_mutex_unlock(&threading_lock);
sleep(0);
pthread_mutex_lock(&threading_lock);
} else {
break;
}
}
signals_waiting = (current_threads.size() - 1);
thread_states.clear();
pid_t tgid = getpid();
pid_t mytid = gettid();
for (pid_t tid : current_threads) {
if (tid == mytid)
continue;
tgkill(tgid, tid, SIGUSR2);
}
// TODO shouldn't busy-wait:
while (signals_waiting) {
pthread_mutex_unlock(&threading_lock);
sleep(0);
pthread_mutex_lock(&threading_lock);
}
assert(num_starting_threads == 0);
return std::move(thread_states);
}
static void _thread_context_dump(int signum, siginfo_t* info, void* _context) {
LockedRegion _lock(&threading_lock);
ucontext_t* context = static_cast<ucontext_t*>(_context);
if (VERBOSITY()) {
pid_t tid = gettid();
printf("in thread_context_dump, tid=%d\n", tid);
printf("%p %p %p\n", context, &context, context->uc_mcontext.fpregs);
printf("old rip: 0x%lx\n", context->uc_mcontext.gregs[REG_RIP]);
}
thread_states.push_back(ThreadState(context->uc_mcontext.gregs));
signals_waiting--; // atomic on std::atomic
}
void registerMainThread() {
LockedRegion _lock(&threading_lock);
current_threads.push_back(gettid());
struct sigaction act = {
.sa_flags = SA_SIGINFO, .sa_sigaction = _thread_context_dump,
};
struct sigaction oldact;
int code = sigaction(SIGUSR2, &act, &oldact);
if (code)
err(1, NULL);
}
static void* _thread_start(void* _arg) {
pid_t tid = gettid();
ThreadStartArgs* arg = static_cast<ThreadStartArgs*>(_arg);
auto start_func = arg->start_func;
Box* arg1 = arg->arg1;
Box* arg2 = arg->arg2;
Box* arg3 = arg->arg3;
delete arg;
{
LockedRegion _lock(&threading_lock);
current_threads.push_back(tid);
num_starting_threads--;
if (VERBOSITY() >= 2)
printf("child initialized; tid=%d\n", tid);
}
threading::GLReadRegion _glock;
return start_func(arg1, arg2, arg3);
}
intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg2, Box* arg3) {
{
LockedRegion _lock(&threading_lock);
num_starting_threads++;
}
ThreadStartArgs* args = new ThreadStartArgs({ .start_func = start_func, .arg1 = arg1, .arg2 = arg2, .arg3 = arg3 });
pthread_t thread_id;
int code = pthread_create(&thread_id, NULL, &_thread_start, args);
assert(code == 0);
if (VERBOSITY() >= 2)
printf("pthread thread_id: 0x%lx\n", thread_id);
static_assert(sizeof(pthread_t) <= sizeof(intptr_t), "");
return thread_id;
}
#if THREADING_USE_GIL
pthread_mutex_t gil = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t gil = PTHREAD_MUTEX_INITIALIZER;
void acquireGLWrite() {
pthread_mutex_lock(&gil);
......@@ -34,9 +188,7 @@ void acquireGLWrite() {
void releaseGLWrite() {
pthread_mutex_unlock(&gil);
}
#endif
} // namespace threading
} // namespace pyston
......@@ -15,9 +15,30 @@
#ifndef PYSTON_CORE_THREADING_H
#define PYSTON_CORE_THREADING_H
#include <cstdint>
#include <cstring>
#include <ucontext.h>
#include <vector>
namespace pyston {
class Box;
namespace threading {
intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg2, Box* arg3);
void registerMainThread();
struct ThreadState {
gregset_t gregs;
ThreadState(gregset_t gregs) { memcpy(this->gregs, gregs, sizeof(gregset_t)); }
};
// Gets a ThreadState per thread, not including the thread calling this function.
// For this call to make sense, the threads all should be blocked;
// as a corollary, this thread is very much not thread safe.
std::vector<ThreadState> getAllThreadStates();
#define THREADING_USE_GIL 1
#define THREADING_USE_GRWL 0
#define THREADING_SAFE_DATASTRUCTURES THREADING_USE_GRWL
......@@ -45,12 +66,12 @@ inline void demoteGL() {
}
#endif
#define MAKE_REGION(name, start, end) \
class name {\
public:\
name() { start(); }\
~name() { end(); }\
};
#define MAKE_REGION(name, start, end) \
class name { \
public: \
name() { start(); } \
~name() { end(); } \
};
MAKE_REGION(GLReadRegion, acquireGLRead, releaseGLRead);
MAKE_REGION(GLPromoteRegion, promoteGL, demoteGL);
......
......@@ -20,6 +20,7 @@
#include "codegen/codegen.h"
#include "core/common.h"
#include "core/threading.h"
#include "core/types.h"
#include "gc/heap.h"
#include "gc/root_finder.h"
......@@ -158,6 +159,11 @@ void runCollection() {
static StatCounter sc("gc_collections");
sc.log();
threading::GLPromoteRegion _lock;
std::vector<threading::ThreadState> threads = threading::getAllThreadStates();
assert(threads.size() == 0);
if (VERBOSITY("gc") >= 2)
printf("Collection #%d\n", ++ncollections);
......
......@@ -108,6 +108,7 @@ int main(int argc, char** argv) {
// end of argument parsing
threading::registerMainThread();
threading::GLReadRegion _glock;
_t.split("to run");
......
......@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <pthread.h>
#include <stddef.h>
#include "codegen/compvars.h"
......@@ -25,21 +24,11 @@ using namespace pyston::threading;
namespace pyston {
static_assert(sizeof(pthread_t) <= sizeof(BoxedInt::n), "");
BoxedModule* thread_module;
static void* thread_start(void* _args) {
threading::GLReadRegion _glock;
std::vector<Box*>* args = static_cast<std::vector<Box*>*>(_args);
assert(args->size() == 2 || args->size() == 3);
Box* target = (*args)[0];
Box* varargs = (*args)[1];
Box* kwargs = NULL;
if (args->size() > 2)
kwargs = (*args)[2];
delete args;
static void* thread_start(Box* target, Box* varargs, Box* kwargs) {
assert(target);
assert(varargs);
try {
runtimeCall(target, ArgPassSpec(0, 0, true, kwargs != NULL), varargs, kwargs, NULL, NULL, NULL);
......@@ -53,9 +42,7 @@ static void* thread_start(void* _args) {
// TODO this should take kwargs, which defaults to empty
Box* startNewThread(Box* target, Box* args) {
pthread_t thread_id;
int code = pthread_create(&thread_id, NULL, &thread_start, new std::vector<Box*>({ target, args }));
assert(code == 0);
intptr_t thread_id = start_thread(&thread_start, target, args, NULL);
return boxInt(thread_id ^ 0x12345678901L);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment