Initial import of coiopp

coiopp grew out of coio library
This commit is contained in:
Moritz Bitsch 2020-06-29 12:03:09 +02:00
commit c925553326
26 changed files with 2842 additions and 0 deletions

30
CMakeLists.txt Normal file
View file

@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.7)
project(coiopp)
add_library(${PROJECT_NAME}
libco/libco.c
coio.cpp
coio_glib.cpp)
add_custom_target(${PROJECT_NAME}_files SOURCES coioimpl.h)
set_target_properties(${PROJECT_NAME} PROPERTIES
C_STANDARD 99
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON)
target_compile_options(${PROJECT_NAME} PRIVATE -W -Wall -Wextra -Werror)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB2 REQUIRED glib-2.0)
pkg_check_modules(GIO2 REQUIRED gio-2.0)
target_include_directories(${PROJECT_NAME} PUBLIC ${GLIB2_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${GLIB2_LIBRARIES})
target_include_directories(${PROJECT_NAME} PRIVATE ${GIO2_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${GIO2_LIBRARIES})
target_link_libraries(${PROJECT_NAME} m)
target_include_directories(${PROJECT_NAME} PUBLIC .)

13
COPYRIGHT Normal file
View file

@ -0,0 +1,13 @@
Copyright (c) 2015 Moritz Bitsch <moritzbitsch@gmail.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

318
Makefile Normal file
View file

@ -0,0 +1,318 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.16
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
# Allow only one "make -f Makefile2" at a time, but pass parallelism.
.NOTPARALLEL:
#=============================================================================
# Special targets provided by cmake.
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES =
.SUFFIXES: .hpux_make_needs_suffix_list
# Suppress display of executed commands.
$(VERBOSE).SILENT:
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
#=============================================================================
# Set environment variables for the build.
# The shell in which to execute make rules.
SHELL = /bin/sh
# The CMake executable.
CMAKE_COMMAND = /usr/bin/cmake
# The command to remove a file.
RM = /usr/bin/cmake -E remove -f
# Escaping for special characters.
EQUALS = =
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /home/moritzbitsch/Repository/Work/commhandler
# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /home/moritzbitsch/Repository/Work/commhandler
#=============================================================================
# Targets provided globally by CMake.
# Special rule for the target install/strip
install/strip: preinstall
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
/usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
.PHONY : install/strip
# Special rule for the target install/strip
install/strip/fast: preinstall/fast
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
/usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
.PHONY : install/strip/fast
# Special rule for the target install/local
install/local: preinstall
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
/usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
.PHONY : install/local
# Special rule for the target install/local
install/local/fast: preinstall/fast
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
/usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
.PHONY : install/local/fast
# Special rule for the target install
install: preinstall
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
/usr/bin/cmake -P cmake_install.cmake
.PHONY : install
# Special rule for the target install
install/fast: preinstall/fast
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
/usr/bin/cmake -P cmake_install.cmake
.PHONY : install/fast
# Special rule for the target list_install_components
list_install_components:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\""
.PHONY : list_install_components
# Special rule for the target list_install_components
list_install_components/fast: list_install_components
.PHONY : list_install_components/fast
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..."
/usr/bin/ccmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : edit_cache
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
# Special rule for the target test
test:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running tests..."
/usr/bin/ctest --force-new-ctest-process $(ARGS)
.PHONY : test
# Special rule for the target test
test/fast: test
.PHONY : test/fast
# Special rule for the target rebuild_cache
rebuild_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
/usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# The main all target
all: cmake_check_build_system
cd /home/moritzbitsch/Repository/Work/commhandler && $(CMAKE_COMMAND) -E cmake_progress_start /home/moritzbitsch/Repository/Work/commhandler/CMakeFiles /home/moritzbitsch/Repository/Work/commhandler/lib/coiopp/CMakeFiles/progress.marks
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f CMakeFiles/Makefile2 lib/coiopp/all
$(CMAKE_COMMAND) -E cmake_progress_start /home/moritzbitsch/Repository/Work/commhandler/CMakeFiles 0
.PHONY : all
# The main clean target
clean:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f CMakeFiles/Makefile2 lib/coiopp/clean
.PHONY : clean
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
preinstall: all
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f CMakeFiles/Makefile2 lib/coiopp/preinstall
.PHONY : preinstall
# Prepare targets for installation.
preinstall/fast:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f CMakeFiles/Makefile2 lib/coiopp/preinstall
.PHONY : preinstall/fast
# clear depends
depend:
cd /home/moritzbitsch/Repository/Work/commhandler && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend
# Convenience name for target.
lib/coiopp/CMakeFiles/coiopp_files.dir/rule:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f CMakeFiles/Makefile2 lib/coiopp/CMakeFiles/coiopp_files.dir/rule
.PHONY : lib/coiopp/CMakeFiles/coiopp_files.dir/rule
# Convenience name for target.
coiopp_files: lib/coiopp/CMakeFiles/coiopp_files.dir/rule
.PHONY : coiopp_files
# fast build rule for target.
coiopp_files/fast:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp_files.dir/build.make lib/coiopp/CMakeFiles/coiopp_files.dir/build
.PHONY : coiopp_files/fast
# Convenience name for target.
lib/coiopp/CMakeFiles/coiopp.dir/rule:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f CMakeFiles/Makefile2 lib/coiopp/CMakeFiles/coiopp.dir/rule
.PHONY : lib/coiopp/CMakeFiles/coiopp.dir/rule
# Convenience name for target.
coiopp: lib/coiopp/CMakeFiles/coiopp.dir/rule
.PHONY : coiopp
# fast build rule for target.
coiopp/fast:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/build
.PHONY : coiopp/fast
coio.o: coio.cpp.o
.PHONY : coio.o
# target to build an object file
coio.cpp.o:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/coio.cpp.o
.PHONY : coio.cpp.o
coio.i: coio.cpp.i
.PHONY : coio.i
# target to preprocess a source file
coio.cpp.i:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/coio.cpp.i
.PHONY : coio.cpp.i
coio.s: coio.cpp.s
.PHONY : coio.s
# target to generate assembly for a file
coio.cpp.s:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/coio.cpp.s
.PHONY : coio.cpp.s
coio_glib.o: coio_glib.cpp.o
.PHONY : coio_glib.o
# target to build an object file
coio_glib.cpp.o:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/coio_glib.cpp.o
.PHONY : coio_glib.cpp.o
coio_glib.i: coio_glib.cpp.i
.PHONY : coio_glib.i
# target to preprocess a source file
coio_glib.cpp.i:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/coio_glib.cpp.i
.PHONY : coio_glib.cpp.i
coio_glib.s: coio_glib.cpp.s
.PHONY : coio_glib.s
# target to generate assembly for a file
coio_glib.cpp.s:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/coio_glib.cpp.s
.PHONY : coio_glib.cpp.s
libco/libco.o: libco/libco.c.o
.PHONY : libco/libco.o
# target to build an object file
libco/libco.c.o:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/libco/libco.c.o
.PHONY : libco/libco.c.o
libco/libco.i: libco/libco.c.i
.PHONY : libco/libco.i
# target to preprocess a source file
libco/libco.c.i:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/libco/libco.c.i
.PHONY : libco/libco.c.i
libco/libco.s: libco/libco.c.s
.PHONY : libco/libco.s
# target to generate assembly for a file
libco/libco.c.s:
cd /home/moritzbitsch/Repository/Work/commhandler && $(MAKE) -f lib/coiopp/CMakeFiles/coiopp.dir/build.make lib/coiopp/CMakeFiles/coiopp.dir/libco/libco.c.s
.PHONY : libco/libco.c.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@echo "... all (the default if no target is provided)"
@echo "... clean"
@echo "... depend"
@echo "... install/strip"
@echo "... install/local"
@echo "... install"
@echo "... list_install_components"
@echo "... coiopp_files"
@echo "... coiopp"
@echo "... edit_cache"
@echo "... test"
@echo "... rebuild_cache"
@echo "... coio.o"
@echo "... coio.i"
@echo "... coio.s"
@echo "... coio_glib.o"
@echo "... coio_glib.i"
@echo "... coio_glib.s"
@echo "... libco/libco.o"
@echo "... libco/libco.i"
@echo "... libco/libco.s"
.PHONY : help
#=============================================================================
# Special targets to cleanup operation of make.
# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
cd /home/moritzbitsch/Repository/Work/commhandler && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system

10
README.md Normal file
View file

@ -0,0 +1,10 @@
# libcoio
a clean, minimal, portable coroutine library
## Compiling
Just run 'make' in the project dir
## License
ISC License except where otherwise noted.
BSD-Style/GPL for coro.c/.h

320
coio.cpp Normal file
View file

@ -0,0 +1,320 @@
/*
* Copyright (c) 2015 Moritz Bitsch <moritzbitsch@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _POSIX_C_SOURCE 200809L
#include <iostream>
#include <math.h>
#include <stdexcept>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "coioimpl.h"
#if defined(__APPLE__)
#include <mach/clock.h>
#include <mach/mach.h>
#endif
CoioTaskList coio_ready_list = { 0, 0 };
CoioTaskList coio_sleeping = { 0, 0 };
cothread_t coio_sched_ctx;
CoioTask* coio_current = NULL;
unsigned long coio_taskcount = 0;
static int
msleep(uvlong ms)
{
struct timespec req, rem;
if (ms > 999) {
req.tv_sec = (int)(ms / 1000);
req.tv_nsec = (ms - ((long)req.tv_sec * 1000)) * 1000000;
}
else {
req.tv_sec = 0;
req.tv_nsec = ms * 1000000;
}
return nanosleep(&req, &rem);
}
static void
_process_events()
{
uvlong now;
int ms = 5;
CoioTask* t;
if ((t = coio_sleeping.head) != NULL && t->timeout != 0) {
now = coio_now();
if (now >= t->timeout) {
ms = 0;
}
else {
ms = (t->timeout - now);
}
}
/* TODO:do I/O polling instead of usleep */
msleep(ms);
/* handle CLOCK_MONOTONIC bugs (VirtualBox anyone?) */
while (!coio_ready_list.head) {
/* wake up timed out tasks */
now = coio_now();
while ((t = coio_sleeping.head) && t->timeout && now >= t->timeout) {
coio_ready(t);
}
}
}
int
coio_main()
{
/* initialize empty ctx for scheduler */
coio_sched_ctx = co_active();
/* scheduler mainloop */
for (;;) {
if (!coio_ready_list.head && coio_sleeping.head)
_process_events();
if (!coio_ready_list.head)
break;
coio_current = coio_ready_list.head;
coio_current->ready = 0;
coio_del(&coio_ready_list, coio_current);
co_switch(coio_current->ctx);
if (coio_current->eptr) {
std::rethrow_exception(coio_current->eptr);
}
if (coio_current->done) {
coio_taskcount--;
co_delete(coio_current->ctx);
delete coio_current;
}
coio_current = nullptr;
}
if (coio_taskcount) {
return -1;
}
return 0;
}
static CoioTask* construction_task = nullptr;
int
coio_create(const char* name, coio_func f, unsigned int stacksize)
{
CoioTask* task;
task = new CoioTask();
if (!task)
return -1;
task->name = name;
task->func = f;
construction_task = task;
task->ctx = co_create(stacksize, []() {
auto task = construction_task;
co_switch(coio_sched_ctx);
try {
task->func();
} catch (...) {
task->eptr = std::current_exception();
}
task->done = 1;
co_switch(coio_sched_ctx);
});
co_switch(task->ctx);
coio_add(&coio_ready_list, task);
coio_taskcount++;
return 0;
}
uvlong
coio_timeout(CoioTask* task, int ms)
{
CoioTask* t;
if (ms >= 0) {
task->timeout = coio_now() + ms;
for (t = coio_sleeping.head;
t != NULL && t->timeout && t->timeout < task->timeout;
t = t->next)
;
}
else {
task->timeout = 0;
t = NULL;
}
if (t) {
task->prev = t->prev;
task->next = t;
}
else {
task->prev = coio_sleeping.tail;
task->next = NULL;
}
t = coio_current;
if (t->prev) {
t->prev->next = t;
}
else {
coio_sleeping.head = t;
}
if (t->next) {
t->next->prev = t;
}
else {
coio_sleeping.tail = t;
}
return task->timeout;
}
int
coio_delay(int ms)
{
uvlong when;
when = coio_timeout(coio_current, ms);
coio_transfer();
return (coio_now() - when);
}
void
coio_yield()
{
coio_ready(coio_current);
coio_transfer();
}
void
coio_add(CoioTaskList* lst, CoioTask* task)
{
if (lst->tail) {
lst->tail->next = task;
task->prev = lst->tail;
}
else {
lst->head = task;
task->prev = NULL;
}
lst->tail = task;
task->next = NULL;
}
void
coio_del(CoioTaskList* lst, CoioTask* task)
{
if (task->prev) {
task->prev->next = task->next;
}
else if (lst->head == task) {
lst->head = task->next;
}
if (task->next) {
task->next->prev = task->prev;
}
else if (lst->tail == task) {
lst->tail = task->prev;
}
}
void
coio_ready(CoioTask* task)
{
task->timeout = 0;
if (!task->ready) {
task->ready = 1;
coio_del(&coio_sleeping, task);
coio_add(&coio_ready_list, task);
}
}
void
coio_transfer()
{
co_switch(coio_sched_ctx);
}
uvlong
coio_now()
{
#if defined(__APPLE__)
clock_serv_t cclock;
mach_timespec_t ts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &ts);
mach_port_deallocate(mach_task_self(), cclock);
#else
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
return -1;
#endif
return (uvlong)ts.tv_sec * 1000 + round(ts.tv_nsec / 1000000.0);
}
void
coio_debug()
{
CoioTask* t;
fprintf(stderr, ">>>\nCurrent tasks: %s\n", coio_current->name);
fprintf(stderr, "Sleeping tasks: ");
for (t = coio_sleeping.head; t != NULL; t = t->next) {
fprintf(stderr, "%s ", t->name);
}
fprintf(stderr, "\nReady tasks: ");
for (t = coio_ready_list.head; t != NULL; t = t->next) {
fprintf(stderr, "%s ", t->name);
}
fprintf(stderr, "\n");
}
bool
coio_active()
{
return coio_current != nullptr;
}
void
coio_active_throw()
{
if (!coio_active()) {
throw std::logic_error("Not inside coroutine");
}
}

45
coio.h Normal file
View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2015 Moritz Bitsch <moritzbitsch@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef COIO_H
#define COIO_H
#include <functional>
typedef struct CoioTask CoioTask;
typedef std::function<void()> coio_func;
typedef unsigned long long uvlong;
extern CoioTask* coio_current;
int
coio_main();
int
coio_create(const char* name, coio_func f, unsigned int stacksize);
void
coio_yield();
uvlong
coio_now();
int
coio_delay(int ms);
void
coio_ready(CoioTask* task);
bool
coio_active();
void
coio_active_throw();
#endif

150
coio_glib.cpp Normal file
View file

@ -0,0 +1,150 @@
/*
* Copyright (c) 2018 Moritz Bitsch <moritzbitsch@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "coio_glib.h"
#include "coio.h"
#include "coioimpl.h"
#include <iostream>
#include <gio/gio.h>
#include <glib.h>
struct coio_source
{
GSource base;
};
static gboolean
coio_source_prepare(GSource* source, gint* timeout_)
{
(void)source;
uvlong now;
int ms = -1;
CoioTask* t;
if (coio_ready_list.head) {
*timeout_ = 0;
/* if we return true here our check functions do not get called */
if (coio_sleeping.head) {
return FALSE;
}
return TRUE;
}
if ((t = coio_sleeping.head) != NULL && t->timeout != 0) {
now = coio_now();
if (now >= t->timeout) {
ms = 0;
}
else {
ms = (t->timeout - now);
}
}
*timeout_ = ms;
return FALSE;
}
static gboolean
coio_source_check(GSource* source)
{
(void)source;
CoioTask* t;
if (coio_sleeping.head) {
/* wake up timed out tasks */
uvlong now = coio_now();
while ((t = coio_sleeping.head) && t->timeout && now >= t->timeout) {
coio_ready(t);
}
}
if (coio_ready_list.head)
return TRUE;
return FALSE;
}
static gboolean
coio_source_dispatch(GSource* source, GSourceFunc callback, gpointer user_data)
{
(void)source;
CoioTask* last;
gboolean result = G_SOURCE_CONTINUE;
if (!coio_ready_list.head)
return G_SOURCE_CONTINUE;
last = coio_ready_list.tail;
do {
coio_current = coio_ready_list.head;
coio_current->ready = 0;
coio_del(&coio_ready_list, coio_current);
co_switch(coio_current->ctx);
/* glib is C, don't let the exception go wild */
try {
if (coio_current->eptr) {
std::rethrow_exception(coio_current->eptr);
}
} catch (const std::exception& ex) {
std::cerr << "Exception in coroutine " << coio_current->name << ": "
<< ex.what() << std::endl;
} catch (...) {
std::cerr << "Unknown exception in coroutine " << coio_current->name
<< std::endl;
}
if (coio_current->done) {
coio_taskcount--;
co_delete(coio_current->ctx);
delete coio_current;
}
} while (coio_current != last);
coio_current = NULL;
if (callback) {
result = callback(user_data);
}
return result;
}
static void
coio_source_finalize(GSource* source)
{
(void)source;
}
GSource*
coio_gsource_create()
{
coio_sched_ctx = co_active();
static GSourceFuncs funcs = { coio_source_prepare,
coio_source_check,
coio_source_dispatch,
coio_source_finalize,
NULL,
NULL };
return g_source_new(&funcs, sizeof(struct coio_source));
}

26
coio_glib.h Normal file
View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 Moritz Bitsch <moritzbitsch@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef COIO_GLIB_H
#define COIO_GLIB_H
#include <glib.h>
#include <gio/gio.h>
#include "coio.h"
GSource* coio_gsource_create();
#endif

64
coioimpl.h Normal file
View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2015 Moritz Bitsch <moritzbitsch@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef COIOIMPL_H
#define COIOIMPL_H
#include "coio.h"
#include "libco/libco.h"
#include <exception>
typedef struct CoioTaskList CoioTaskList;
struct CoioTask
{
cothread_t ctx;
const char* name;
coio_func func;
uvlong timeout;
int ready;
int done;
std::exception_ptr eptr;
/* linked list support */
CoioTask* next;
CoioTask* prev;
};
struct CoioTaskList
{
CoioTask* head;
CoioTask* tail;
};
extern CoioTaskList coio_ready_list;
extern CoioTaskList coio_sleeping;
extern cothread_t coio_sched_ctx;
extern unsigned long coio_taskcount;
void
coio_add(CoioTaskList* lst, CoioTask* task);
void
coio_del(CoioTaskList* lst, CoioTask* task);
void
coio_transfer();
void
coio_debug();
#endif

107
libco/aarch64.c Normal file
View file

@ -0,0 +1,107 @@
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local unsigned long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (*co_swap)(cothread_t, cothread_t) = 0;
#ifdef LIBCO_MPROTECT
alignas(4096)
#else
section(text)
#endif
static const uint32_t co_swap_function[1024] = {
0xa9002428, /* stp x8,x9,[x1] */
0xa9012c2a, /* stp x10,x11,[x1,#16] */
0xa902342c, /* stp x12,x13,[x1,#32] */
0xa9033c2e, /* stp x14,x15,[x1,#48] */
0xf9002433, /* str x19,[x1,#72] */
0xa9055434, /* stp x20,x21,[x1,#80] */
0xa9065c36, /* stp x22,x23,[x1,#96] */
0xa9076438, /* stp x24,x25,[x1,#112] */
0xa9086c3a, /* stp x26,x27,[x1,#128] */
0xa909743c, /* stp x28,x29,[x1,#144] */
0x910003f0, /* mov x16,sp */
0xa90a7830, /* stp x16,x30,[x1,#160] */
0xa9402408, /* ldp x8,x9,[x0] */
0xa9412c0a, /* ldp x10,x11,[x0,#16] */
0xa942340c, /* ldp x12,x13,[x0,#32] */
0xa9433c0e, /* ldp x14,x15,[x0,#48] */
0xf9402413, /* ldr x19,[x0,#72] */
0xa9455414, /* ldp x20,x21,[x0,#80] */
0xa9465c16, /* ldp x22,x23,[x0,#96] */
0xa9476418, /* ldp x24,x25,[x0,#112] */
0xa9486c1a, /* ldp x26,x27,[x0,#128] */
0xa949741c, /* ldp x28,x29,[x0,#144] */
0xa94a4410, /* ldp x16,x17,[x0,#160] */
0x9100021f, /* mov sp,x16 */
0xd61f0220, /* br x17 */
};
static void co_init() {
#ifdef LIBCO_MPROTECT
unsigned long addr = (unsigned long)co_swap_function;
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_EXEC);
#endif
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) {
unsigned long* handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
if(handle = (unsigned long*)memory) {
unsigned int offset = (size & ~15);
unsigned long* p = (unsigned long*)((unsigned char*)handle + offset);
handle[19] = (unsigned long)p; /* x29 (frame pointer) */
handle[20] = (unsigned long)p; /* x30 (stack pointer) */
handle[21] = (unsigned long)entrypoint; /* x31 (link register) */
}
return handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
int co_serializable() {
return 1;
}
#ifdef __cplusplus
}
#endif

163
libco/amd64.c Executable file
View file

@ -0,0 +1,163 @@
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local long long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (*co_swap)(cothread_t, cothread_t) = 0;
#ifdef LIBCO_MPROTECT
alignas(4096)
#else
section(text)
#endif
#ifdef _WIN32
/* ABI: Win64 */
static const unsigned char co_swap_function[4096] = {
0x48, 0x89, 0x22, /* mov [rdx],rsp */
0x48, 0x8b, 0x21, /* mov rsp,[rcx] */
0x58, /* pop rax */
0x48, 0x89, 0x6a, 0x08, /* mov [rdx+ 8],rbp */
0x48, 0x89, 0x72, 0x10, /* mov [rdx+16],rsi */
0x48, 0x89, 0x7a, 0x18, /* mov [rdx+24],rdi */
0x48, 0x89, 0x5a, 0x20, /* mov [rdx+32],rbx */
0x4c, 0x89, 0x62, 0x28, /* mov [rdx+40],r12 */
0x4c, 0x89, 0x6a, 0x30, /* mov [rdx+48],r13 */
0x4c, 0x89, 0x72, 0x38, /* mov [rdx+56],r14 */
0x4c, 0x89, 0x7a, 0x40, /* mov [rdx+64],r15 */
#if !defined(LIBCO_NO_SSE)
0x0f, 0x29, 0x72, 0x50, /* movaps [rdx+ 80],xmm6 */
0x0f, 0x29, 0x7a, 0x60, /* movaps [rdx+ 96],xmm7 */
0x44, 0x0f, 0x29, 0x42, 0x70, /* movaps [rdx+112],xmm8 */
0x48, 0x83, 0xc2, 0x70, /* add rdx,112 */
0x44, 0x0f, 0x29, 0x4a, 0x10, /* movaps [rdx+ 16],xmm9 */
0x44, 0x0f, 0x29, 0x52, 0x20, /* movaps [rdx+ 32],xmm10 */
0x44, 0x0f, 0x29, 0x5a, 0x30, /* movaps [rdx+ 48],xmm11 */
0x44, 0x0f, 0x29, 0x62, 0x40, /* movaps [rdx+ 64],xmm12 */
0x44, 0x0f, 0x29, 0x6a, 0x50, /* movaps [rdx+ 80],xmm13 */
0x44, 0x0f, 0x29, 0x72, 0x60, /* movaps [rdx+ 96],xmm14 */
0x44, 0x0f, 0x29, 0x7a, 0x70, /* movaps [rdx+112],xmm15 */
#endif
0x48, 0x8b, 0x69, 0x08, /* mov rbp,[rcx+ 8] */
0x48, 0x8b, 0x71, 0x10, /* mov rsi,[rcx+16] */
0x48, 0x8b, 0x79, 0x18, /* mov rdi,[rcx+24] */
0x48, 0x8b, 0x59, 0x20, /* mov rbx,[rcx+32] */
0x4c, 0x8b, 0x61, 0x28, /* mov r12,[rcx+40] */
0x4c, 0x8b, 0x69, 0x30, /* mov r13,[rcx+48] */
0x4c, 0x8b, 0x71, 0x38, /* mov r14,[rcx+56] */
0x4c, 0x8b, 0x79, 0x40, /* mov r15,[rcx+64] */
#if !defined(LIBCO_NO_SSE)
0x0f, 0x28, 0x71, 0x50, /* movaps xmm6, [rcx+ 80] */
0x0f, 0x28, 0x79, 0x60, /* movaps xmm7, [rcx+ 96] */
0x44, 0x0f, 0x28, 0x41, 0x70, /* movaps xmm8, [rcx+112] */
0x48, 0x83, 0xc1, 0x70, /* add rcx,112 */
0x44, 0x0f, 0x28, 0x49, 0x10, /* movaps xmm9, [rcx+ 16] */
0x44, 0x0f, 0x28, 0x51, 0x20, /* movaps xmm10,[rcx+ 32] */
0x44, 0x0f, 0x28, 0x59, 0x30, /* movaps xmm11,[rcx+ 48] */
0x44, 0x0f, 0x28, 0x61, 0x40, /* movaps xmm12,[rcx+ 64] */
0x44, 0x0f, 0x28, 0x69, 0x50, /* movaps xmm13,[rcx+ 80] */
0x44, 0x0f, 0x28, 0x71, 0x60, /* movaps xmm14,[rcx+ 96] */
0x44, 0x0f, 0x28, 0x79, 0x70, /* movaps xmm15,[rcx+112] */
#endif
0xff, 0xe0, /* jmp rax */
};
#include <windows.h>
static void co_init() {
#ifdef LIBCO_MPROTECT
DWORD old_privileges;
VirtualProtect((void*)co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READ, &old_privileges);
#endif
}
#else
/* ABI: SystemV */
static const unsigned char co_swap_function[4096] = {
0x48, 0x89, 0x26, /* mov [rsi],rsp */
0x48, 0x8b, 0x27, /* mov rsp,[rdi] */
0x58, /* pop rax */
0x48, 0x89, 0x6e, 0x08, /* mov [rsi+ 8],rbp */
0x48, 0x89, 0x5e, 0x10, /* mov [rsi+16],rbx */
0x4c, 0x89, 0x66, 0x18, /* mov [rsi+24],r12 */
0x4c, 0x89, 0x6e, 0x20, /* mov [rsi+32],r13 */
0x4c, 0x89, 0x76, 0x28, /* mov [rsi+40],r14 */
0x4c, 0x89, 0x7e, 0x30, /* mov [rsi+48],r15 */
0x48, 0x8b, 0x6f, 0x08, /* mov rbp,[rdi+ 8] */
0x48, 0x8b, 0x5f, 0x10, /* mov rbx,[rdi+16] */
0x4c, 0x8b, 0x67, 0x18, /* mov r12,[rdi+24] */
0x4c, 0x8b, 0x6f, 0x20, /* mov r13,[rdi+32] */
0x4c, 0x8b, 0x77, 0x28, /* mov r14,[rdi+40] */
0x4c, 0x8b, 0x7f, 0x30, /* mov r15,[rdi+48] */
0xff, 0xe0, /* jmp rax */
};
#include <unistd.h>
#include <sys/mman.h>
static void co_init() {
#ifdef LIBCO_MPROTECT
unsigned long long addr = (unsigned long long)co_swap_function;
unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_EXEC);
#endif
}
#endif
static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
if((handle = (cothread_t)memory)) {
unsigned int offset = (size & ~15) - 32;
long long *p = (long long*)((char*)handle + offset); /* seek to top of stack */
*--p = (long long)crash; /* crash if entrypoint returns */
*--p = (long long)entrypoint; /* start of function */
*(long long*)handle = (long long)p; /* stack pointer */
}
return handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
int co_serializable() {
return 1;
}
#ifdef __cplusplus
}
#endif

82
libco/arm.c Normal file
View file

@ -0,0 +1,82 @@
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local unsigned long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (*co_swap)(cothread_t, cothread_t) = 0;
#ifdef LIBCO_MPROTECT
alignas(4096)
#else
section(text)
#endif
static const unsigned long co_swap_function[1024] = {
0xe8a16ff0, /* stmia r1!, {r4-r11,sp,lr} */
0xe8b0aff0, /* ldmia r0!, {r4-r11,sp,pc} */
0xe12fff1e, /* bx lr */
};
static void co_init() {
#ifdef LIBCO_MPROTECT
unsigned long addr = (unsigned long)co_swap_function;
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_EXEC);
#endif
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) {
unsigned long* handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
if((handle = (unsigned long*)memory)) {
unsigned int offset = (size & ~15);
unsigned long* p = (unsigned long*)((unsigned char*)handle + offset);
handle[8] = (unsigned long)p;
handle[9] = (unsigned long)entrypoint;
}
return handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
int co_serializable() {
return 1;
}
#ifdef __cplusplus
}
#endif

12
libco/doc/style.css Executable file
View file

@ -0,0 +1,12 @@
body {
background: #333;
color: #fff;
}
code {
background: #444;
}
a {
color: #aaf;
}

89
libco/doc/targets.html Executable file
View file

@ -0,0 +1,89 @@
<html>
<head>
<title></title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<b>Supported targets:</b><br/><br/>
Note that supported targets are only those that have been tested and confirmed
working. It is quite possible that libco will work on more processors, compilers
and operating systems than those listed below.
<hr/>
<b>libco.x86</b><br/>
Overhead: ~5x<br/>
Supported processor(s): 32-bit x86<br/>
Supported compiler(s): any<br/>
Supported operating system(s):<ul>
<li>Windows</li>
<li>Mac OS X</li>
<li>Linux</li>
<li>BSD</li>
</ul>
<hr/>
<b>libco.amd64</b><br/>
Overhead: ~10x (Windows), ~6x (all other platforms)<br/>
Supported processor(s): 64-bit amd64<br/>
Supported compiler(s): any<br/>
Supported operating system(s):<ul>
<li>Windows</li>
<li>Mac OS X</li>
<li>Linux</li>
<li>BSD</li>
</ul>
<hr/>
<b>libco.ppc</b><br/>
Overhead: ~20x<br/>
Supported processor(s): 32-bit PowerPC, 64-bit PowerPC<br/>
Supported compiler(s): GNU GCC<br/>
Supported operating system(s):<ul>
</ul>
<li>Mac OS X</li>
<li>Linux</li>
<li>BSD</li>
<li>Playstation 3</li>
</ul>
<br/>
Note: this module contains compiler flags to enable/disable FPU and Altivec
support.
<hr/>
<b>libco.fiber</b><br/>
Overhead: ~15x<br/>
Supported processor(s): Processor independent<br/>
Supported compiler(s): any<br/>
Supported operating system(s):<ul>
<li>Windows</li>
</ul>
<hr/>
<b>libco.sjlj</b><br/>
Overhead: ~30x<br/>
Supported processor(s): Processor independent<br/>
Supported compiler(s): any<br/>
Supported operating system(s):<ul>
<li>Mac OS X</li>
<li>Linux</li>
<li>BSD</li>
<li>Solaris</li>
</ul>
<hr/>
<b>libco.ucontext</b><br/>
Overhead: <b><font color="#ff0000">~300x</font></b><br/>
Supported processor(s): Processor independent<br/>
Supported compiler(s): any<br/>
Supported operating system(s):<ul>
<li>Linux</li>
<li>BSD</li>
</ul>
<hr/>
</body>
</html>

108
libco/doc/usage.html Executable file
View file

@ -0,0 +1,108 @@
<html>
<head>
<title></title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<b>License:</b><br/><br/>
libco is released under the ISC license.
<hr/>
<b>Foreword:</b><br/><br/>
libco is a cross-platform, permissively licensed implementation of
cooperative-multithreading; a feature that is sorely lacking from the ISO C/C++
standard.<br/>
The library is designed for maximum speed and portability, and not for safety or
features. If safety or extra functionality is desired, a wrapper API can easily
be written to encapsulate all library functions.<br/>
Behavior of executing operations that are listed as not permitted below result
in undefined behavior. They may work anyway, they may cause undesired / unknown
behavior, or they may crash the program entirely.<br/>
The goal of this library was to simplify the base API as much as possible,
implementing only that which cannot be implemented using pure C. Additional
functionality after this would only complicate ports of this library to new
platforms.
<hr/>
<b>Porting:</b><br/><br/>
This document is included as a reference for porting libco. Please submit any
ports you create to me, so that libco can become more useful. Please note that
since libco is permissively licensed, you must submit your code as a work of the
public domain in order for it to be included in the official distribution.
Full credit will be given in the source code of the official release. Please
do not bother submitting code to me under any other license -- including GPL,
LGPL, BSD or CC -- I am not interested in creating a library with multiple
different licenses depending on which targets are used.
<hr/>
<b>Synopsis:</b><br/><br/>
<code>
typedef void* cothread_t;<br/>
<br/>
cothread_t co_active();<br/>
cothread_t co_create(unsigned int heapsize, void (*coentry)(void));<br/>
void &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;co_delete(cothread_t cothread);<br/>
void &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;co_switch(cothread_t cothread);<br/>
</code>
<hr/>
<b>Usage:</b>
<hr/>
<code>typedef void* cothread_t;</code><br/><br/>
Handle to cothread.<br/>
Handle must be of type void*.<br/>
A value of null (0) indicates an uninitialized or invalid
handle, whereas a non-zero value indicates a valid handle.
<hr/>
<code>cothread_t co_active();</code><br/><br/>
Return handle to current cothread. Always returns a valid handle, even when
called from the main program thread.
<hr/>
<code>cothread_t co_derive(void* memory, unsigned int heapsize, void (*coentry)(void));</code><br/><br/>
Initializes new cothread.</br>
This function is identical to co_create, only it attempts to use the provided
memory instead of allocating new memory on the heap. Please note that certain
implementations (currently only Windows Fibers) cannot be created using existing
memory, and as such, this function will fail.
<hr/>
<code>cothread_t co_create(unsigned int heapsize, void (*coentry)(void));</code><br/><br/>
Create new cothread.<br/>
Heapsize is the amount of memory allocated for the cothread stack, specified
in bytes. This is unfortunately impossible to make fully portable. It is
recommended to specify sizes using `n * sizeof(void*)'. It is better to err
on the side of caution and allocate more memory than will be needed to ensure
compatibility with other platforms, within reason. A typical heapsize for a
32-bit architecture is ~1MB.<br/>
When the new cothread is first called, program execution jumps to coentry.
This function does not take any arguments, due to portability issues with
passing function arguments. However, arguments can be simulated by the use
of global variables, which can be set before the first call to each cothread.<br/>
coentry() must not return, and should end with an appropriate co_switch()
statement. Behavior is undefined if entry point returns normally.<br/>
Library is responsible for allocating cothread stack memory, to free
the user from needing to allocate special memory capable of being used
as program stack memory on platforms where this is required.<br/>
User is always responsible for deleting cothreads with co_delete().<br/>
Return value of null (0) indicates cothread creation failed.
<hr/>
<code>void co_delete(cothread_t cothread);</code><br/><br/>
Delete specified cothread.<br/>
Null (0) or invalid cothread handle is not allowed.<br/>
Passing handle of active cothread to this function is not allowed.<br/>
Passing handle of primary cothread is not allowed.
<hr/>
<code>void co_switch(cothread_t cothread);</code><br/><br/>
Switch to specified cothread.<br/>
Null (0) or invalid cothread handle is not allowed.<br/>
Passing handle of active cothread to this function is not allowed.
<hr/>
</body>
</html>

55
libco/fiber.c Executable file
View file

@ -0,0 +1,55 @@
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#define WINVER 0x0400
#define _WIN32_WINNT 0x0400
#include <windows.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local cothread_t co_active_ = 0;
static void __stdcall co_thunk(void* coentry) {
((void (*)(void))coentry)();
}
cothread_t co_active() {
if(!co_active_) {
ConvertThreadToFiber(0);
co_active_ = GetCurrentFiber();
}
return co_active_;
}
cothread_t co_derive(void* memory, unsigned int heapsize, void (*coentry)(void)) {
//Windows fibers do not allow users to supply their own memory
return (cothread_t)0;
}
cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) {
if(!co_active_) {
ConvertThreadToFiber(0);
co_active_ = GetCurrentFiber();
}
return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry);
}
void co_delete(cothread_t cothread) {
DeleteFiber(cothread);
}
void co_switch(cothread_t cothread) {
co_active_ = cothread;
SwitchToFiber(cothread);
}
int co_serializable() {
return 0;
}
#ifdef __cplusplus
}
#endif

37
libco/libco.c Executable file
View file

@ -0,0 +1,37 @@
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wparentheses"
/* placing code in section(text) does not mark it executable with Clang. */
#undef LIBCO_MPROTECT
#define LIBCO_MPROTECT
#endif
#if defined(__clang__) || defined(__GNUC__)
#if defined(__i386__)
#include "x86.c"
#elif defined(__amd64__)
#include "amd64.c"
#elif defined(__arm__)
#include "arm.c"
#elif defined(__aarch64__)
#include "aarch64.c"
#elif defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
#include "ppc64v2.c"
#elif defined(_ARCH_PPC) && !defined(__LITTLE_ENDIAN__)
#include "ppc.c"
#elif defined(_WIN32)
#include "fiber.c"
#else
#include "sjlj.c"
#endif
#elif defined(_MSC_VER)
#if defined(_M_IX86)
#include "x86.c"
#elif defined(_M_AMD64)
#include "amd64.c"
#else
#include "fiber.c"
#endif
#else
#error "libco: unsupported processor, compiler or operating system"
#endif

28
libco/libco.h Executable file
View file

@ -0,0 +1,28 @@
/*
libco v20 (2019-10-16)
author: byuu
license: ISC
*/
#ifndef LIBCO_H
#define LIBCO_H
#ifdef __cplusplus
extern "C" {
#endif
typedef void* cothread_t;
cothread_t co_active();
cothread_t co_derive(void*, unsigned int, void (*)(void));
cothread_t co_create(unsigned int, void (*)(void));
void co_delete(cothread_t);
void co_switch(cothread_t);
int co_serializable();
#ifdef __cplusplus
}
#endif
/* ifndef LIBCO_H */
#endif

431
libco/ppc.c Executable file
View file

@ -0,0 +1,431 @@
/* ppc64le (ELFv2) is not currently supported */
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#if LIBCO_MPROTECT
#include <unistd.h>
#include <sys/mman.h>
#endif
/* state format (offsets in 32-bit words)
+0 pointer to swap code
rest of function descriptor for entry function
+8 PC
+10 SP
special registers
GPRs
FPRs
VRs
stack
*/
enum { state_size = 1024 };
enum { above_stack = 2048 };
enum { stack_align = 256 };
static thread_local cothread_t co_active_handle = 0;
/* determine environment */
#define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__)
/* whether function calls are indirect through a descriptor, or are directly to function */
#ifndef LIBCO_PPCDESC
#if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || (LIBCO_PPC64 && (!defined(_CALL_ELF) || _CALL_ELF == 1)))
#define LIBCO_PPCDESC 1
#endif
#endif
#ifdef LIBCO_MPROTECT
alignas(4096)
#else
section(text)
#endif
static const uint32_t libco_ppc_code[1024] = {
#if LIBCO_PPC64
0x7d000026, /* mfcr r8 */
0xf8240028, /* std r1,40(r4) */
0x7d2802a6, /* mflr r9 */
0xf9c40048, /* std r14,72(r4) */
0xf9e40050, /* std r15,80(r4) */
0xfa040058, /* std r16,88(r4) */
0xfa240060, /* std r17,96(r4) */
0xfa440068, /* std r18,104(r4) */
0xfa640070, /* std r19,112(r4) */
0xfa840078, /* std r20,120(r4) */
0xfaa40080, /* std r21,128(r4) */
0xfac40088, /* std r22,136(r4) */
0xfae40090, /* std r23,144(r4) */
0xfb040098, /* std r24,152(r4) */
0xfb2400a0, /* std r25,160(r4) */
0xfb4400a8, /* std r26,168(r4) */
0xfb6400b0, /* std r27,176(r4) */
0xfb8400b8, /* std r28,184(r4) */
0xfba400c0, /* std r29,192(r4) */
0xfbc400c8, /* std r30,200(r4) */
0xfbe400d0, /* std r31,208(r4) */
0xf9240020, /* std r9,32(r4) */
0xe8e30020, /* ld r7,32(r3) */
0xe8230028, /* ld r1,40(r3) */
0x48000009, /* bl 1 */
0x7fe00008, /* trap */
0x91040030, /*1:stw r8,48(r4) */
0x80c30030, /* lwz r6,48(r3) */
0x7ce903a6, /* mtctr r7 */
0xe9c30048, /* ld r14,72(r3) */
0xe9e30050, /* ld r15,80(r3) */
0xea030058, /* ld r16,88(r3) */
0xea230060, /* ld r17,96(r3) */
0xea430068, /* ld r18,104(r3) */
0xea630070, /* ld r19,112(r3) */
0xea830078, /* ld r20,120(r3) */
0xeaa30080, /* ld r21,128(r3) */
0xeac30088, /* ld r22,136(r3) */
0xeae30090, /* ld r23,144(r3) */
0xeb030098, /* ld r24,152(r3) */
0xeb2300a0, /* ld r25,160(r3) */
0xeb4300a8, /* ld r26,168(r3) */
0xeb6300b0, /* ld r27,176(r3) */
0xeb8300b8, /* ld r28,184(r3) */
0xeba300c0, /* ld r29,192(r3) */
0xebc300c8, /* ld r30,200(r3) */
0xebe300d0, /* ld r31,208(r3) */
0x7ccff120, /* mtcr r6 */
#else
0x7d000026, /* mfcr r8 */
0x90240028, /* stw r1,40(r4) */
0x7d2802a6, /* mflr r9 */
0x91a4003c, /* stw r13,60(r4) */
0x91c40040, /* stw r14,64(r4) */
0x91e40044, /* stw r15,68(r4) */
0x92040048, /* stw r16,72(r4) */
0x9224004c, /* stw r17,76(r4) */
0x92440050, /* stw r18,80(r4) */
0x92640054, /* stw r19,84(r4) */
0x92840058, /* stw r20,88(r4) */
0x92a4005c, /* stw r21,92(r4) */
0x92c40060, /* stw r22,96(r4) */
0x92e40064, /* stw r23,100(r4) */
0x93040068, /* stw r24,104(r4) */
0x9324006c, /* stw r25,108(r4) */
0x93440070, /* stw r26,112(r4) */
0x93640074, /* stw r27,116(r4) */
0x93840078, /* stw r28,120(r4) */
0x93a4007c, /* stw r29,124(r4) */
0x93c40080, /* stw r30,128(r4) */
0x93e40084, /* stw r31,132(r4) */
0x91240020, /* stw r9,32(r4) */
0x80e30020, /* lwz r7,32(r3) */
0x80230028, /* lwz r1,40(r3) */
0x48000009, /* bl 1 */
0x7fe00008, /* trap */
0x91040030, /*1:stw r8,48(r4) */
0x80c30030, /* lwz r6,48(r3) */
0x7ce903a6, /* mtctr r7 */
0x81a3003c, /* lwz r13,60(r3) */
0x81c30040, /* lwz r14,64(r3) */
0x81e30044, /* lwz r15,68(r3) */
0x82030048, /* lwz r16,72(r3) */
0x8223004c, /* lwz r17,76(r3) */
0x82430050, /* lwz r18,80(r3) */
0x82630054, /* lwz r19,84(r3) */
0x82830058, /* lwz r20,88(r3) */
0x82a3005c, /* lwz r21,92(r3) */
0x82c30060, /* lwz r22,96(r3) */
0x82e30064, /* lwz r23,100(r3) */
0x83030068, /* lwz r24,104(r3) */
0x8323006c, /* lwz r25,108(r3) */
0x83430070, /* lwz r26,112(r3) */
0x83630074, /* lwz r27,116(r3) */
0x83830078, /* lwz r28,120(r3) */
0x83a3007c, /* lwz r29,124(r3) */
0x83c30080, /* lwz r30,128(r3) */
0x83e30084, /* lwz r31,132(r3) */
0x7ccff120, /* mtcr r6 */
#endif
#ifndef LIBCO_PPC_NOFP
0xd9c400e0, /* stfd f14,224(r4) */
0xd9e400e8, /* stfd f15,232(r4) */
0xda0400f0, /* stfd f16,240(r4) */
0xda2400f8, /* stfd f17,248(r4) */
0xda440100, /* stfd f18,256(r4) */
0xda640108, /* stfd f19,264(r4) */
0xda840110, /* stfd f20,272(r4) */
0xdaa40118, /* stfd f21,280(r4) */
0xdac40120, /* stfd f22,288(r4) */
0xdae40128, /* stfd f23,296(r4) */
0xdb040130, /* stfd f24,304(r4) */
0xdb240138, /* stfd f25,312(r4) */
0xdb440140, /* stfd f26,320(r4) */
0xdb640148, /* stfd f27,328(r4) */
0xdb840150, /* stfd f28,336(r4) */
0xdba40158, /* stfd f29,344(r4) */
0xdbc40160, /* stfd f30,352(r4) */
0xdbe40168, /* stfd f31,360(r4) */
0xc9c300e0, /* lfd f14,224(r3) */
0xc9e300e8, /* lfd f15,232(r3) */
0xca0300f0, /* lfd f16,240(r3) */
0xca2300f8, /* lfd f17,248(r3) */
0xca430100, /* lfd f18,256(r3) */
0xca630108, /* lfd f19,264(r3) */
0xca830110, /* lfd f20,272(r3) */
0xcaa30118, /* lfd f21,280(r3) */
0xcac30120, /* lfd f22,288(r3) */
0xcae30128, /* lfd f23,296(r3) */
0xcb030130, /* lfd f24,304(r3) */
0xcb230138, /* lfd f25,312(r3) */
0xcb430140, /* lfd f26,320(r3) */
0xcb630148, /* lfd f27,328(r3) */
0xcb830150, /* lfd f28,336(r3) */
0xcba30158, /* lfd f29,344(r3) */
0xcbc30160, /* lfd f30,352(r3) */
0xcbe30168, /* lfd f31,360(r3) */
#endif
#ifdef __ALTIVEC__
0x7ca042a6, /* mfvrsave r5 */
0x39040180, /* addi r8,r4,384 */
0x39240190, /* addi r9,r4,400 */
0x70a00fff, /* andi. r0,r5,4095 */
0x90a40034, /* stw r5,52(r4) */
0x4182005c, /* beq- 2 */
0x7e8041ce, /* stvx v20,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7ea049ce, /* stvx v21,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7ec041ce, /* stvx v22,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7ee049ce, /* stvx v23,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7f0041ce, /* stvx v24,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7f2049ce, /* stvx v25,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7f4041ce, /* stvx v26,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7f6049ce, /* stvx v27,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7f8041ce, /* stvx v28,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7fa049ce, /* stvx v29,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7fc041ce, /* stvx v30,r0,r8 */
0x7fe049ce, /* stvx v31,r0,r9 */
0x80a30034, /*2:lwz r5,52(r3) */
0x39030180, /* addi r8,r3,384 */
0x39230190, /* addi r9,r3,400 */
0x70a00fff, /* andi. r0,r5,4095 */
0x7ca043a6, /* mtvrsave r5 */
0x4d820420, /* beqctr */
0x7e8040ce, /* lvx v20,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7ea048ce, /* lvx v21,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7ec040ce, /* lvx v22,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7ee048ce, /* lvx v23,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7f0040ce, /* lvx v24,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7f2048ce, /* lvx v25,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7f4040ce, /* lvx v26,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7f6048ce, /* lvx v27,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7f8040ce, /* lvx v28,r0,r8 */
0x39080020, /* addi r8,r8,32 */
0x7fa048ce, /* lvx v29,r0,r9 */
0x39290020, /* addi r9,r9,32 */
0x7fc040ce, /* lvx v30,r0,r8 */
0x7fe048ce, /* lvx v31,r0,r9 */
#endif
0x4e800420, /* bctr */
};
#if LIBCO_PPCDESC
/* function call goes through indirect descriptor */
#define CO_SWAP_ASM(x, y) ((void (*)(cothread_t, cothread_t))(uintptr_t)x)(x, y)
#else
/* function call goes directly to code */
#define CO_SWAP_ASM(x, y) ((void (*)(cothread_t, cothread_t))(uintptr_t)libco_ppc_code)(x, y)
#endif
static uint32_t* co_derive_(void* memory, unsigned size, uintptr_t entry) {
(void)entry;
uint32_t* t = (uint32_t*)memory;
#if LIBCO_PPCDESC
if(t) {
memcpy(t, (void*)entry, sizeof(void*) * 3); /* copy entry's descriptor */
*(const void**)t = libco_ppc_code; /* set function pointer to swap routine */
}
#endif
return t;
}
cothread_t co_derive(void* memory, unsigned int size, void (*entry_)(void)) {
uintptr_t entry = (uintptr_t)entry_;
uint32_t* t = 0;
/* be sure main thread was successfully allocated */
if(co_active()) {
t = co_derive_(memory, size, entry);
}
if(t) {
uintptr_t sp;
int shift;
/* save current registers into new thread, so that any special ones will have proper values when thread is begun */
CO_SWAP_ASM(t, t);
#if LIBCO_PPCDESC
entry = (uintptr_t)*(void**)entry; /* get real address */
#endif
/* put stack near end of block, and align */
sp = (uintptr_t)t + size - above_stack;
sp -= sp % stack_align;
/* on PPC32, we save and restore GPRs as 32 bits. for PPC64, we
save and restore them as 64 bits, regardless of the size the ABI
uses. so, we manually write pointers at the proper size. we always
save and restore at the same address, and since PPC is big-endian,
we must put the low byte first on PPC32. */
/* if uintptr_t is 32 bits, >>32 is undefined behavior,
so we do two shifts and don't have to care how many bits uintptr_t is. */
#if LIBCO_PPC64
shift = 16;
#else
shift = 0;
#endif
/* set up so entry will be called on next swap */
t[ 8] = (uint32_t)(entry >> shift >> shift);
t[ 9] = (uint32_t)entry;
t[10] = (uint32_t)(sp >> shift >> shift);
t[11] = (uint32_t)sp;
}
return t;
}
static uint32_t* co_create_(unsigned size, uintptr_t entry) {
(void)entry;
uint32_t* t = (uint32_t*)malloc(size);
#if LIBCO_PPCDESC
if(t) {
memcpy(t, (void*)entry, sizeof(void*) * 3); /* copy entry's descriptor */
*(const void**)t = libco_ppc_code; /* set function pointer to swap routine */
}
#endif
return t;
}
cothread_t co_create(unsigned int size, void (*entry_)(void)) {
uintptr_t entry = (uintptr_t)entry_;
uint32_t* t = 0;
/* be sure main thread was successfully allocated */
if(co_active()) {
size += state_size + above_stack + stack_align;
t = co_create_(size, entry);
}
if(t) {
uintptr_t sp;
int shift;
/* save current registers into new thread, so that any special ones will have proper values when thread is begun */
CO_SWAP_ASM(t, t);
#if LIBCO_PPCDESC
entry = (uintptr_t)*(void**)entry; /* get real address */
#endif
/* put stack near end of block, and align */
sp = (uintptr_t)t + size - above_stack;
sp -= sp % stack_align;
/* on PPC32, we save and restore GPRs as 32 bits. for PPC64, we
save and restore them as 64 bits, regardless of the size the ABI
uses. so, we manually write pointers at the proper size. we always
save and restore at the same address, and since PPC is big-endian,
we must put the low byte first on PPC32. */
/* if uintptr_t is 32 bits, >>32 is undefined behavior,
so we do two shifts and don't have to care how many bits uintptr_t is. */
#if LIBCO_PPC64
shift = 16;
#else
shift = 0;
#endif
/* set up so entry will be called on next swap */
t[ 8] = (uint32_t)(entry >> shift >> shift);
t[ 9] = (uint32_t)entry;
t[10] = (uint32_t)(sp >> shift >> shift);
t[11] = (uint32_t)sp;
}
return t;
}
void co_delete(cothread_t t) {
free(t);
}
static void co_init_(void) {
#if LIBCO_MPROTECT
long page_size = sysconf(_SC_PAGESIZE);
if(page_size > 0) {
uintptr_t align = page_size;
uintptr_t begin = (uintptr_t)libco_ppc_code;
uintptr_t end = begin + sizeof libco_ppc_code;
/* align beginning and end */
end += align - 1;
end -= end % align;
begin -= begin % align;
mprotect((void*)begin, end - begin, PROT_READ | PROT_EXEC);
}
#endif
co_active_handle = co_create_(state_size, (uintptr_t)&co_switch);
}
cothread_t co_active() {
if(!co_active_handle) co_init_();
return co_active_handle;
}
void co_switch(cothread_t t) {
cothread_t old = co_active_handle;
co_active_handle = t;
CO_SWAP_ASM(t, old);
}
int co_serializable() {
return 0;
}

279
libco/ppc64v2.c Normal file
View file

@ -0,0 +1,279 @@
/* author: Shawn Anastasio */
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
struct ppc64_context {
//GPRs
uint64_t gprs[32];
uint64_t lr;
uint64_t ccr;
//FPRs
uint64_t fprs[32];
#ifdef __ALTIVEC__
//Altivec (VMX)
uint64_t vmx[12 * 2];
uint32_t vrsave;
#endif
};
static thread_local struct ppc64_context* co_active_handle = 0;
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define ALIGN(p, x) ((void*)((uintptr_t)(p) & ~((x) - 1)))
#define MIN_STACK 0x10000lu
#define MIN_STACK_FRAME 0x20lu
#define STACK_ALIGN 0x10lu
void swap_context(struct ppc64_context* read, struct ppc64_context* write);
__asm__(
".text\n"
".align 4\n"
".type swap_context @function\n"
"swap_context:\n"
".cfi_startproc\n"
//save GPRs
"std 1, 8(4)\n"
"std 2, 16(4)\n"
"std 12, 96(4)\n"
"std 13, 104(4)\n"
"std 14, 112(4)\n"
"std 15, 120(4)\n"
"std 16, 128(4)\n"
"std 17, 136(4)\n"
"std 18, 144(4)\n"
"std 19, 152(4)\n"
"std 20, 160(4)\n"
"std 21, 168(4)\n"
"std 22, 176(4)\n"
"std 23, 184(4)\n"
"std 24, 192(4)\n"
"std 25, 200(4)\n"
"std 26, 208(4)\n"
"std 27, 216(4)\n"
"std 28, 224(4)\n"
"std 29, 232(4)\n"
"std 30, 240(4)\n"
"std 31, 248(4)\n"
//save LR
"mflr 5\n"
"std 5, 256(4)\n"
//save CCR
"mfcr 5\n"
"std 5, 264(4)\n"
//save FPRs
"stfd 14, 384(4)\n"
"stfd 15, 392(4)\n"
"stfd 16, 400(4)\n"
"stfd 17, 408(4)\n"
"stfd 18, 416(4)\n"
"stfd 19, 424(4)\n"
"stfd 20, 432(4)\n"
"stfd 21, 440(4)\n"
"stfd 22, 448(4)\n"
"stfd 23, 456(4)\n"
"stfd 24, 464(4)\n"
"stfd 25, 472(4)\n"
"stfd 26, 480(4)\n"
"stfd 27, 488(4)\n"
"stfd 28, 496(4)\n"
"stfd 29, 504(4)\n"
"stfd 30, 512(4)\n"
"stfd 31, 520(4)\n"
#ifdef __ALTIVEC__
//save VMX
"li 5, 528\n"
"stvxl 20, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 21, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 22, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 23, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 24, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 25, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 26, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 27, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 28, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 29, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 30, 4, 5\n"
"addi 5, 5, 16\n"
"stvxl 31, 4, 5\n"
"addi 5, 5, 16\n"
//save VRSAVE
"mfvrsave 5\n"
"stw 5, 736(4)\n"
#endif
//restore GPRs
"ld 1, 8(3)\n"
"ld 2, 16(3)\n"
"ld 12, 96(3)\n"
"ld 13, 104(3)\n"
"ld 14, 112(3)\n"
"ld 15, 120(3)\n"
"ld 16, 128(3)\n"
"ld 17, 136(3)\n"
"ld 18, 144(3)\n"
"ld 19, 152(3)\n"
"ld 20, 160(3)\n"
"ld 21, 168(3)\n"
"ld 22, 176(3)\n"
"ld 23, 184(3)\n"
"ld 24, 192(3)\n"
"ld 25, 200(3)\n"
"ld 26, 208(3)\n"
"ld 27, 216(3)\n"
"ld 28, 224(3)\n"
"ld 29, 232(3)\n"
"ld 30, 240(3)\n"
"ld 31, 248(3)\n"
//restore LR
"ld 5, 256(3)\n"
"mtlr 5\n"
//restore CCR
"ld 5, 264(3)\n"
"mtcr 5\n"
//restore FPRs
"lfd 14, 384(3)\n"
"lfd 15, 392(3)\n"
"lfd 16, 400(3)\n"
"lfd 17, 408(3)\n"
"lfd 18, 416(3)\n"
"lfd 19, 424(3)\n"
"lfd 20, 432(3)\n"
"lfd 21, 440(3)\n"
"lfd 22, 448(3)\n"
"lfd 23, 456(3)\n"
"lfd 24, 464(3)\n"
"lfd 25, 472(3)\n"
"lfd 26, 480(3)\n"
"lfd 27, 488(3)\n"
"lfd 28, 496(3)\n"
"lfd 29, 504(3)\n"
"lfd 30, 512(3)\n"
"lfd 31, 520(3)\n"
#ifdef __ALTIVEC__
//restore VMX
"li 5, 528\n"
"lvxl 20, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 21, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 22, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 23, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 24, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 25, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 26, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 27, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 28, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 29, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 30, 3, 5\n"
"addi 5, 5, 16\n"
"lvxl 31, 3, 5\n"
"addi 5, 5, 16\n"
//restore VRSAVE
"lwz 5, 720(3)\n"
"mtvrsave 5\n"
#endif
//branch to LR
"blr\n"
".cfi_endproc\n"
".size swap_context, .-swap_context\n"
);
cothread_t co_active() {
if(!co_active_handle) {
co_active_handle = (struct ppc64_context*)malloc(MIN_STACK + sizeof(struct ppc64_context));
}
return (cothread_t)co_active_handle;
}
cothread_t co_derive(void* memory, unsigned int size, void (*coentry)(void)) {
uint8_t* sp;
struct ppc64_context* context = (struct ppc64_context*)memory;
//save current context into new context to initialize it
swap_context(context, context);
//align stack
sp = (uint8_t*)memory + size - STACK_ALIGN;
sp = (uint8_t*)ALIGN(sp, STACK_ALIGN);
//write 0 for initial backchain
*(uint64_t*)sp = 0;
//create new frame with backchain
sp -= MIN_STACK_FRAME;
*(uint64_t*)sp = (uint64_t)(sp + MIN_STACK_FRAME);
//update context with new stack (r1) and entrypoint (r12, lr)
context->gprs[ 1] = (uint64_t)sp;
context->gprs[12] = (uint64_t)coentry;
context->lr = (uint64_t)coentry;
return (cothread_t)memory;
}
cothread_t co_create(unsigned int size, void (*coentry)(void)) {
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, coentry);
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t to) {
struct ppc64_context* from = co_active_handle;
co_active_handle = (struct ppc64_context*)to;
swap_context((struct ppc64_context*)to, from);
}
int co_serializable() {
return 1;
}
#ifdef __cplusplus
}
#endif

38
libco/settings.h Normal file
View file

@ -0,0 +1,38 @@
#if defined(LIBCO_C)
/*[amd64, arm, ppc, x86]:
by default, co_swap_function is marked as a text (code) section
if not supported, uncomment the below line to use mprotect instead */
/* #define LIBCO_MPROTECT */
/*[amd64]:
Win64 only: provides a substantial speed-up, but will thrash XMM regs
do not use this unless you are certain your application won't use SSE */
/* #define LIBCO_NO_SSE */
#if defined(LIBCO_C)
#if defined(LIBCO_MP)
#define thread_local __thread
#else
#define thread_local
#endif
#endif
#if __STDC_VERSION__ >= 201112L
#if !defined(_MSC_VER)
#include <stdalign.h>
#endif
#else
#define alignas(bytes)
#endif
#if defined(_MSC_VER)
#define section(name) __declspec(allocate("." #name))
#elif defined(__APPLE__)
#define section(name) __attribute__((section("__TEXT,__" #name)))
#else
#define section(name) __attribute__((section("." #name "#")))
#endif
/* if defined(LIBCO_C) */
#endif

145
libco/sjlj.c Executable file
View file

@ -0,0 +1,145 @@
/*
note this was designed for UNIX systems. Based on ideas expressed in a paper by Ralf Engelschall.
for SJLJ on other systems, one would want to rewrite springboard() and co_create() and hack the jmb_buf stack pointer.
*/
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#define _BSD_SOURCE
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
sigjmp_buf context;
void (*coentry)(void);
void* stack;
} cothread_struct;
static thread_local cothread_struct co_primary;
static thread_local cothread_struct* creating;
static thread_local cothread_struct* co_running = 0;
static void springboard(int ignored) {
if(sigsetjmp(creating->context, 0)) {
co_running->coentry();
}
}
cothread_t co_active() {
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;
}
cothread_t co_derive(void* memory, unsigned int size, void (*coentry)(void)) {
if(!co_running) co_running = &co_primary;
cothread_struct* thread = (cothread_struct*)memory;
memory = (unsigned char*)memory + sizeof(cothread_struct);
size -= sizeof(cothread_struct);
if(thread) {
struct sigaction handler;
struct sigaction old_handler;
stack_t stack;
stack_t old_stack;
thread->coentry = thread->stack = 0;
stack.ss_flags = 0;
stack.ss_size = size;
thread->stack = stack.ss_sp = memory;
if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) {
handler.sa_handler = springboard;
handler.sa_flags = SA_ONSTACK;
sigemptyset(&handler.sa_mask);
creating = thread;
if(!sigaction(SIGUSR1, &handler, &old_handler)) {
if(!raise(SIGUSR1)) {
thread->coentry = coentry;
}
sigaltstack(&old_stack, 0);
sigaction(SIGUSR1, &old_handler, 0);
}
}
if(thread->coentry != coentry) {
co_delete(thread);
thread = 0;
}
}
return (cothread_t)thread;
}
cothread_t co_create(unsigned int size, void (*coentry)(void)) {
if(!co_running) co_running = &co_primary;
cothread_struct* thread = (cothread_struct*)malloc(sizeof(cothread_struct));
if(thread) {
struct sigaction handler;
struct sigaction old_handler;
stack_t stack;
stack_t old_stack;
thread->coentry = thread->stack = 0;
stack.ss_flags = 0;
stack.ss_size = size;
thread->stack = stack.ss_sp = malloc(size);
if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) {
handler.sa_handler = springboard;
handler.sa_flags = SA_ONSTACK;
sigemptyset(&handler.sa_mask);
creating = thread;
if(!sigaction(SIGUSR1, &handler, &old_handler)) {
if(!raise(SIGUSR1)) {
thread->coentry = coentry;
}
sigaltstack(&old_stack, 0);
sigaction(SIGUSR1, &old_handler, 0);
}
}
if(thread->coentry != coentry) {
co_delete(thread);
thread = 0;
}
}
return (cothread_t)thread;
}
void co_delete(cothread_t cothread) {
if(cothread) {
if(((cothread_struct*)cothread)->stack) {
free(((cothread_struct*)cothread)->stack);
}
free(cothread);
}
}
void co_switch(cothread_t cothread) {
if(!sigsetjmp(co_running->context, 0)) {
co_running = (cothread_struct*)cothread;
siglongjmp(co_running->context, 1);
}
}
int co_serializable() {
return 0;
}
#ifdef __cplusplus
}
#endif

86
libco/ucontext.c Executable file
View file

@ -0,0 +1,86 @@
/*
WARNING: the overhead of POSIX ucontext is very high,
assembly versions of libco or libco_sjlj should be much faster
this library only exists for two reasons:
1: as an initial test for the viability of a ucontext implementation
2: to demonstrate the power and speed of libco over existing implementations,
such as pth (which defaults to wrapping ucontext on unix targets)
use this library only as a *last resort*
*/
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#define _BSD_SOURCE
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <ucontext.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local ucontext_t co_primary;
static thread_local ucontext_t* co_running = 0;
cothread_t co_active() {
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;
}
cothread_t co_derive(void* memory, unsigned int heapsize, void (*coentry)(void)) {
if(!co_running) co_running = &co_primary;
ucontext_t* thread = (ucontext_t*)memory;
memory = (unsigned char*)memory + sizeof(ucontext_t);
heapsize -= sizeof(ucontext_t);
if(thread) {
if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = memory)) {
thread->uc_link = co_running;
thread->uc_stack.ss_size = heapsize;
makecontext(thread, coentry, 0);
} else {
thread = 0;
}
}
return (cothread_t)thread;
}
cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) {
if(!co_running) co_running = &co_primary;
ucontext_t* thread = (ucontext_t*)malloc(sizeof(ucontext_t));
if(thread) {
if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) {
thread->uc_link = co_running;
thread->uc_stack.ss_size = heapsize;
makecontext(thread, coentry, 0);
} else {
co_delete((cothread_t)thread);
thread = 0;
}
}
return (cothread_t)thread;
}
void co_delete(cothread_t cothread) {
if(cothread) {
if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); }
free(cothread);
}
}
void co_switch(cothread_t cothread) {
ucontext_t* old_thread = co_running;
co_running = (ucontext_t*)cothread;
swapcontext(old_thread, co_running);
}
int co_serializable() {
return 0;
}
#ifdef __cplusplus
}
#endif

117
libco/x86.c Executable file
View file

@ -0,0 +1,117 @@
#define LIBCO_C
#include "libco.h"
#include "settings.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__clang__) || defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#elif defined(_MSC_VER)
#define fastcall __fastcall
#else
#error "libco: please define fastcall macro"
#endif
static thread_local long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
#ifdef LIBCO_MPROTECT
alignas(4096)
#else
section(text)
#endif
/* ABI: fastcall */
static const unsigned char co_swap_function[4096] = {
0x89, 0x22, /* mov [edx],esp */
0x8b, 0x21, /* mov esp,[ecx] */
0x58, /* pop eax */
0x89, 0x6a, 0x04, /* mov [edx+ 4],ebp */
0x89, 0x72, 0x08, /* mov [edx+ 8],esi */
0x89, 0x7a, 0x0c, /* mov [edx+12],edi */
0x89, 0x5a, 0x10, /* mov [edx+16],ebx */
0x8b, 0x69, 0x04, /* mov ebp,[ecx+ 4] */
0x8b, 0x71, 0x08, /* mov esi,[ecx+ 8] */
0x8b, 0x79, 0x0c, /* mov edi,[ecx+12] */
0x8b, 0x59, 0x10, /* mov ebx,[ecx+16] */
0xff, 0xe0, /* jmp eax */
};
#ifdef _WIN32
#include <windows.h>
static void co_init() {
#ifdef LIBCO_MPROTECT
DWORD old_privileges;
VirtualProtect((void*)co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READ, &old_privileges);
#endif
}
#else
#include <unistd.h>
#include <sys/mman.h>
static void co_init() {
#ifdef LIBCO_MPROTECT
unsigned long addr = (unsigned long)co_swap_function;
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_EXEC);
#endif
}
#endif
static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
if(handle = (cothread_t)memory) {
unsigned int offset = (size & ~15) - 32;
long *p = (long*)((char*)handle + offset); /* seek to top of stack */
*--p = (long)crash; /* crash if entrypoint returns */
*--p = (long)entrypoint; /* start of function */
*(long*)handle = (long)p; /* stack pointer */
}
return handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
void* memory = malloc(size);
if(!memory) return (cothread_t)0;
return co_derive(memory, size, entrypoint);
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
int co_serializable() {
return 1;
}
#ifdef __cplusplus
}
#endif

40
testdelay.c Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2015 Moritz Bitsch <moritzbitsch@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include "coio.h"
void
_t1(void* arg)
{
printf("going to sleep 1000ms (1s)\n");
coio_delay(1000);
printf("woken up\n");
}
int
main(int argc, char** argv)
{
(void) argc;
(void) argv;
coio_create("t1", _t1, NULL, 0x8000);
if (coio_main() < 0) {
printf("Deadlocked\n");
return 1;
}
return 0;
}

49
testyield.c Normal file
View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015 Moritz Bitsch <moritzbitsch@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include "coio.h"
void
_t1(void* arg)
{
printf("Hello 1 from _t1\n");
coio_yield();
printf("Hello 2 from _t1\n");
}
void
_t2(void* arg)
{
printf("Hello 1 from _t2\n");
coio_yield();
printf("Hello 2 from _t2\n");
}
int
main(int argc, char** argv)
{
(void) argc;
(void) argv;
coio_create("t1", _t1, NULL, 0x8000);
coio_create("t2", _t2, NULL, 0x8000);
if (coio_main() < 0) {
printf("Deadlocked\n");
return 1;
}
return 0;
}