Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 933e4d6e authored by Sharvil Nanavati's avatar Sharvil Nanavati
Browse files

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
parent ce09e055
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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
+35 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  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);
+117 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  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 <assert.h>
#include <pthread.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <utils/Log.h>

#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;
}
+48 −0
Original line number Diff line number Diff line
#include <gtest/gtest.h>

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);
}