From 118bdd58389c77dd69b37e377b4e98ea89d2b14e Mon Sep 17 00:00:00 2001 From: Sharvil Nanavati Date: Wed, 7 May 2014 22:09:12 -0700 Subject: [PATCH] Start of threading library So far it's a thin shim around pthreads which allows setting thread name and querying tids from any thread. Change-Id: Id156f662778806a54a8a302be424ee051fac4710 --- osi/Android.mk | 6 ++- osi/include/thread.h | 35 ++++++++++++++ osi/src/thread.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++ osi/test/thread_test.cpp | 48 +++++++++++++++++++ 4 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 osi/include/thread.h create mode 100644 osi/src/thread.c create mode 100644 osi/test/thread_test.cpp diff --git a/osi/Android.mk b/osi/Android.mk index 646209c6c..5d2f5e3b9 100644 --- a/osi/Android.mk +++ b/osi/Android.mk @@ -10,7 +10,8 @@ LOCAL_SRC_FILES := \ ./src/fixed_queue.c \ ./src/list.c \ ./src/reactor.c \ - ./src/semaphore.c + ./src/semaphore.c \ + ./src/thread.c LOCAL_CFLAGS := -std=c99 -Wall -Werror LOCAL_MODULE := libosi @@ -30,7 +31,8 @@ LOCAL_C_INCLUDES := \ LOCAL_SRC_FILES := \ ./test/config_test.cpp \ ./test/list_test.cpp \ - ./test/reactor_test.cpp + ./test/reactor_test.cpp \ + ./test/thread_test.cpp LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE := ositests diff --git a/osi/include/thread.h b/osi/include/thread.h new file mode 100644 index 000000000..8558e92ec --- /dev/null +++ b/osi/include/thread.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#pragma once + +#define THREAD_NAME_MAX 16 + +struct thread_t; +typedef struct thread_t thread_t; + +typedef void *(*thread_start_cb) (void *); + +// Lifecycle +thread_t *thread_create(const char *name, + thread_start_cb start_routine, void *arg); +int thread_join(thread_t *thread, void **retval); + +// Query +pid_t thread_id(const thread_t *thread); +const char *thread_name(const thread_t *thread); diff --git a/osi/src/thread.c b/osi/src/thread.c new file mode 100644 index 000000000..b81e901ca --- /dev/null +++ b/osi/src/thread.c @@ -0,0 +1,117 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Google, Inc. + * + * 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. + * + ******************************************************************************/ + +#define LOG_TAG "osi_thread" + +#include +#include +#include +#include +#include +#include + +#include "semaphore.h" +#include "thread.h" + +typedef struct thread_t { + pthread_t pthread; + pid_t tid; + char name[THREAD_NAME_MAX+1]; +} thread_t; + +pid_t thread_id(const thread_t *thread) { + assert(thread != NULL); + return thread->tid; +} + +const char *thread_name(const thread_t *thread) { + assert(thread != NULL); + return thread->name; +} + +struct start_arg { + thread_t *thread; + semaphore_t *start_sem; + int error; + thread_start_cb start_routine; + void *arg; +}; + +static void *run_thread(void *start_arg) { + assert(start_arg != NULL); + + struct start_arg *start = start_arg; + thread_t *thread = start->thread; + + assert(thread != NULL); + + if (prctl(PR_SET_NAME, (unsigned long)thread->name) == -1) { + ALOGE("%s unable to set thread name: %s", __func__, strerror(errno)); + start->error = errno; + semaphore_post(start->start_sem); + return NULL; + } + thread->tid = gettid(); + + // Cache local values because we are about to let thread_create + // continue + thread_start_cb start_routine = start->start_routine; + void *arg = start->arg; + + semaphore_post(start->start_sem); + return start_routine(arg); +} + +thread_t *thread_create(const char *name, + thread_start_cb start_routine, void *arg) { + assert(name != NULL); + assert(start_routine != NULL); + + // Start is on the stack, but we use a semaphore, so it's safe + struct start_arg start; + thread_t *ret; + ret = calloc(1, sizeof(thread_t)); + if (!ret) + goto error; + start.start_sem = semaphore_new(0); + if (!start.start_sem) + goto error; + + strncpy(ret->name, name, THREAD_NAME_MAX); + start.thread = ret; + start.error = 0; + start.start_routine = start_routine; + start.arg = arg; + pthread_create(&ret->pthread, NULL, run_thread, &start); + semaphore_wait(start.start_sem); + if (start.error) + goto error; + return ret; + +error:; + semaphore_free(start.start_sem); + free(ret); + return NULL; +} + +int thread_join(thread_t *thread, void **retval) { + int ret = pthread_join(thread->pthread, retval); + if (!ret) + free(thread); + return ret; +} diff --git a/osi/test/thread_test.cpp b/osi/test/thread_test.cpp new file mode 100644 index 000000000..bf4c03c68 --- /dev/null +++ b/osi/test/thread_test.cpp @@ -0,0 +1,48 @@ +#include + +extern "C" { +#include "thread.h" +#include "osi.h" +} + +void *start_routine(void *arg) +{ + return arg; +} + +TEST(ThreadTest, test_new_simple) { + thread_t *thread = thread_create("test_thread", &start_routine, NULL); + ASSERT_TRUE(thread != NULL); + thread_join(thread, NULL); +} + +TEST(ThreadTest, test_join_simple) { + thread_t *thread = thread_create("test_thread", &start_routine, NULL); + thread_join(thread, NULL); +} + +TEST(ThreadTest, test_name) { + thread_t *thread = thread_create("test_name", &start_routine, NULL); + ASSERT_STREQ(thread_name(thread), "test_name"); + thread_join(thread, NULL); +} + +TEST(ThreadTest, test_long_name) { + thread_t *thread = thread_create("0123456789abcdef", &start_routine, NULL); + ASSERT_STREQ("0123456789abcdef", thread_name(thread)); + thread_join(thread, NULL); +} + +TEST(ThreadTest, test_very_long_name) { + thread_t *thread = thread_create("0123456789abcdefg", &start_routine, NULL); + ASSERT_STREQ("0123456789abcdef", thread_name(thread)); + thread_join(thread, NULL); +} + +TEST(ThreadTest, test_return) { + int arg = 10; + void *ret; + thread_t *thread = thread_create("test", &start_routine, &arg); + thread_join(thread, &ret); + ASSERT_EQ(ret, &arg); +} -- 2.11.0