blob: fcca5aa72b79cae041b4cf302712189931f5ac55 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2011-2021 Arm Limited
//
// 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.
// ----------------------------------------------------------------------------
/**
* @brief Platform-specific function implementations.
*
* This module contains functions with strongly OS-dependent implementations:
*
* * CPU count queries
* * Threading
* * Time
*
* In addition to the basic thread abstraction (which is native pthreads on
* all platforms, except Windows where it is an emulation of pthreads), a
* utility function to create N threads and wait for them to complete a batch
* task has also been provided.
*/
#include "astcenccli_internal.h"
/* ============================================================================
Platform code for Windows using the Win32 APIs.
============================================================================ */
#if defined(_WIN32) && !defined(__CYGWIN__)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
/** @brief Alias pthread_t to one of the internal Windows types. */
typedef HANDLE pthread_t;
/** @brief Alias pthread_attr_t to one of the internal Windows types. */
typedef int pthread_attr_t;
/**
* @brief Proxy Windows @c CreateThread underneath a pthreads-like wrapper.
*/
static int pthread_create(
pthread_t* thread,
const pthread_attr_t* attribs,
void* (*threadfunc)(void*),
void* thread_arg
) {
static_cast<void>(attribs);
LPTHREAD_START_ROUTINE func = reinterpret_cast<LPTHREAD_START_ROUTINE>(threadfunc);
*thread = CreateThread(nullptr, 0, func, thread_arg, 0, nullptr);
return 0;
}
/**
* @brief Proxy Windows @c WaitForSingleObject underneath a pthreads-like wrapper.
*/
static int pthread_join(
pthread_t thread,
void** value
) {
static_cast<void>(value);
WaitForSingleObject(thread, INFINITE);
return 0;
}
/* See header for documentation */
int get_cpu_count()
{
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
}
/* See header for documentation */
double get_time()
{
FILETIME tv;
GetSystemTimePreciseAsFileTime(&tv);
unsigned long long ticks = tv.dwHighDateTime;
ticks = (ticks << 32) | tv.dwLowDateTime;
return static_cast<double>(ticks) / 1.0e7;
}
/* ============================================================================
Platform code for an platform using POSIX APIs.
============================================================================ */
#else
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
/* See header for documentation */
int get_cpu_count()
{
return static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
}
/* See header for documentation */
double get_time()
{
timeval tv;
gettimeofday(&tv, 0);
return static_cast<double>(tv.tv_sec) + static_cast<double>(tv.tv_usec) * 1.0e-6;
}
#endif
/**
* @brief Worker thread helper payload for launch_threads.
*/
struct launch_desc
{
/** @brief The native thread handle. */
pthread_t thread_handle;
/** @brief The total number of threads in the thread pool. */
int thread_count;
/** @brief The thread index in the thread pool. */
int thread_id;
/** @brief The user thread function to execute. */
void (*func)(int, int, void*);
/** @brief The user thread payload. */
void* payload;
};
/**
* @brief Helper function to translate thread entry points.
*
* Convert a (void*) thread entry to an (int, void*) thread entry, where the
* integer contains the thread ID in the thread pool.
*
* @param p The thread launch helper payload.
*/
static void* launch_threads_helper(
void *p
) {
launch_desc* ltd = reinterpret_cast<launch_desc*>(p);
ltd->func(ltd->thread_count, ltd->thread_id, ltd->payload);
return nullptr;
}
/* See header for documentation */
void launch_threads(
int thread_count,
void (*func)(int, int, void*),
void *payload
) {
// Directly execute single threaded workloads on this thread
if (thread_count <= 1)
{
func(1, 0, payload);
return;
}
// Otherwise spawn worker threads
launch_desc *thread_descs = new launch_desc[thread_count];
for (int i = 0; i < thread_count; i++)
{
thread_descs[i].thread_count = thread_count;
thread_descs[i].thread_id = i;
thread_descs[i].payload = payload;
thread_descs[i].func = func;
pthread_create(&(thread_descs[i].thread_handle), nullptr,
launch_threads_helper, reinterpret_cast<void*>(thread_descs + i));
}
// ... and then wait for them to complete
for (int i = 0; i < thread_count; i++)
{
pthread_join(thread_descs[i].thread_handle, nullptr);
}
delete[] thread_descs;
}