blob: fb248d5ae1cea4aec81066526401a12f2036fc42 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Saves and clears register context on the current thread.
// For use with blocking IRT calls.
#include <errno.h>
#include <pthread.h>
#include "private/bionic_atomic_inline.h"
#include "private/pthread_context.h"
#include "pthread_internal.h"
extern "C" {
void __pthread_save_context_regs(void* regs, int size) {
pthread_internal_t* thread = __get_thread();
memcpy(thread->context_regs, regs, size);
thread->has_context_regs = 1;
ANDROID_MEMBAR_FULL();
}
void __pthread_clear_context_regs() {
pthread_internal_t* thread = __get_thread();
thread->has_context_regs = 0;
ANDROID_MEMBAR_FULL();
}
static bool obtain_lock(bool try_lock) {
if (try_lock) {
// Ideally, we could also check that the mutex is async-safe:
// ((g_thread_list_lock & MUTEX_TYPE_MASK) == MUTEX_TYPE_BITS_NORMAL)
if (pthread_mutex_trylock(&g_thread_list_lock))
return false;
} else {
pthread_mutex_lock(&g_thread_list_lock);
}
return true;
}
int __pthread_get_thread_count(bool try_lock) {
if (!obtain_lock(try_lock))
return -1;
int count = 0;
pthread_internal_t* thread = g_thread_list;
while (thread) {
++count;
thread = thread->next;
}
pthread_mutex_unlock(&g_thread_list_lock);
return count;
}
static void copy_thread_info(__pthread_context_info_t* dst,
const pthread_internal_t* src) {
dst->stack_base = NULL;
dst->has_context_regs = 0;
// Copy the stack boundaries.
if (src->attr.stack_base) {
dst->stack_base =
reinterpret_cast<char*>(src->attr.stack_base) +
src->attr.guard_size;
dst->stack_size = src->attr.stack_size - src->attr.guard_size;
}
// Copy registers, then do a second (racy) read of has_context_regs.
if (src->has_context_regs) {
memcpy(dst->context_regs, src->context_regs,
sizeof(dst->context_regs));
ANDROID_MEMBAR_FULL();
dst->has_context_regs = src->has_context_regs;
}
}
void __pthread_get_current_thread_info(__pthread_context_info_t* info) {
pthread_internal_t* cur_thread = __get_thread();
copy_thread_info(info, cur_thread);
}
int __pthread_get_thread_infos(
bool try_lock, bool include_current,
int max_info_count, __pthread_context_info_t* infos) {
if (!obtain_lock(try_lock))
return -1;
int idx = 0;
pthread_internal_t* cur_thread = __get_thread();
for (pthread_internal_t* thread = g_thread_list;
thread && idx < max_info_count; thread = thread->next) {
if (!include_current && thread == cur_thread)
continue;
copy_thread_info(infos + idx, thread);
if (infos[idx].stack_base)
++idx;
}
pthread_mutex_unlock(&g_thread_list_lock);
return idx;
}
} // extern "C"