Giter Site home page Giter Site logo

richiesams / fibertaskinglib Goto Github PK

View Code? Open in Web Editor NEW
919.0 49.0 80.0 8.43 MB

A library for enabling task-based multi-threading. It allows execution of task graphs with arbitrary dependencies.

License: Apache License 2.0

C++ 83.85% CMake 6.28% C 0.82% Makefile 1.25% Python 7.80%
cpp multithreading fibers task-scheduler coroutines

fibertaskinglib's Introduction

Fiber Tasking Lib

This is a library for enabling task-based multi-threading. It allows execution of task graphs with arbitrary dependencies. Dependencies are represented as atomic counters.

Under the covers, the task graph is executed using fibers, which in turn, are run on a pool of worker threads (one thread per CPU core). This allows the scheduler to wait on dependencies without task chaining or context switches.

This library was first created as a proof of concept of the ideas presented by Christian Gyrling in his 2015 GDC Talk 'Parallelizing the Naughty Dog Engine Using Fibers'


Example

#include "ftl/task_scheduler.h"
#include "ftl/wait_group.h"

#include <assert.h>
#include <stdint.h>

struct NumberSubset {
	uint64_t start;
	uint64_t end;

	uint64_t total;
};

void AddNumberSubset(ftl::TaskScheduler *taskScheduler, void *arg) {
	(void)taskScheduler;
	NumberSubset *subset = reinterpret_cast<NumberSubset *>(arg);

	subset->total = 0;

	while (subset->start != subset->end) {
		subset->total += subset->start;
		++subset->start;
	}

	subset->total += subset->end;
}

/**
 * Calculates the value of a triangle number by dividing the additions up into tasks
 *
 * A triangle number is defined as:
 *         Tn = 1 + 2 + 3 + ... + n
 *
 * The code is checked against the numerical solution which is:
 *         Tn = n * (n + 1) / 2
 */
int main() {
	// Create the task scheduler and bind the main thread to it
	ftl::TaskScheduler taskScheduler;
	taskScheduler.Init();

	// Define the constants to test
	constexpr uint64_t triangleNum = 47593243ULL;
	constexpr uint64_t numAdditionsPerTask = 10000ULL;
	constexpr uint64_t numTasks = (triangleNum + numAdditionsPerTask - 1ULL) / numAdditionsPerTask;

	// Create the tasks
	// FTL allows you to create Tasks on the stack.
	// However, in this case, that would cause a stack overflow
	ftl::Task *tasks = new ftl::Task[numTasks];
	NumberSubset *subsets = new NumberSubset[numTasks];
	uint64_t nextNumber = 1ULL;

	for (uint64_t i = 0ULL; i < numTasks; ++i) {
		NumberSubset *subset = &subsets[i];

		subset->start = nextNumber;
		subset->end = nextNumber + numAdditionsPerTask - 1ULL;
		if (subset->end > triangleNum) {
			subset->end = triangleNum;
		}

		tasks[i] = { AddNumberSubset, subset };

		nextNumber = subset->end + 1;
	}

	// Schedule the tasks
	ftl::WaitGroup wg(&taskScheduler);
	taskScheduler.AddTasks(numTasks, tasks, ftl::TaskPriority::Normal, &wg);

	// FTL creates its own copies of the tasks, so we can safely delete the memory
	delete[] tasks;

	// Wait for the tasks to complete
	wg.Wait();

	// Add the results
	uint64_t result = 0ULL;
	for (uint64_t i = 0; i < numTasks; ++i) {
		result += subsets[i].total;
	}

	// Test
	assert(triangleNum * (triangleNum + 1ULL) / 2ULL == result);
	(void)result;

	// Cleanup
	delete[] subsets;

	// The destructor of TaskScheduler will shut down all the worker threads
	// and unbind the main thread

	return 0;
}


How it works

For a great introduction to fiber and the general idea, I would suggest watching Christian Gyrling’s talk. It’s free to watch (as of the time of writing) from the GDC vault. That said, I will give an overview of how this library works below:

What are fibers

A fiber consists of a stack and a small storage space for registers. It’s a very lightweight execution context that runs inside a thread. You can think of it as a shell of an actual thread.

Why go though the hassle though? What’s the benefit?

The beauty of fibers is that you can switch between them extremely quickly. Ultimately, a switch consists of saving out registers, then swapping the execution pointer and the stack pointer. This is much much faster than a full-on thread context switch.

How do fibers apply to task-based multithreading?

To answer this question, let’s compare to another task-based multithreading library: Intel’s Threading Building Blocks. TBB is an extremely well polished and successful tasking library. It can handle really complex task graphs and has an excellent scheduler. However, let’s imagine a scenario:

  1. Task A creates Tasks B, C, and D and sends them to the scheduler

  2. Task A does some other work, but then it hits the dependency: B, C, and D must be finished.

  3. If they aren’t finished, we can do 2 things:

    1. Spin-wait / Sleep

    2. Ask the scheduler for a new task and start executing that

  4. Let’s take the second path

  5. So the scheduler gives us Task G and we start executing

  6. But Task G ends up needing a dependency as well, so we ask the scheduler for another new task

  7. And another, and another

  8. In the meantime, Tasks B, C, and D have completed

  9. Task A could theoretically be continued, but it’s buried in the stack under the tasks that we got while we were waiting

  10. The only way we can resume A is to wait for the entire chain to unravel back to it, or suffer a context switch.

Now, obviously, this is a contrived example. And as I said above, TBB has an awesome scheduler that works hard to alleviate this problem. That said, fibers can help to eliminate the problem altogether by allowing cheap switching between tasks. This allows us to isolate the execution of one task from another, preventing the 'chaining' effect described above.


The Architecture from 10,000 ft

Task Queue - An 'ordinary' queue for holding the tasks that are waiting to be executed. In the current code, there is a "high priority" queue, and a "low priority" queue.

Fiber Pool - A pool of fibers used for switching to new tasks while the current task is waiting on a dependency. Fibers execute the tasks

Worker Threads - 1 per logical CPU core. These run the fibers.

Waiting Tasks - All the fibers / tasks that are waiting for a dependency to be fufilled. Dependencies are represented with WaitGroups

Tasks can be created on the stack. They’re just a simple struct with a function pointer and an optional void *arg to be passed to the function:

struct Task {
    TaskFunction Function;
    void *ArgData;
};
Task tasks[10];
for (uint i = 0; i < 10; ++i) {
    tasks[i] = {MyFunctionPointer, myFunctionArg};
}

You schedule a task for execution by calling TaskScheduler::AddTasks()

ftl::WaitGroup wg(taskScheduler);
taskScheduler->AddTasks(10, tasks, ftl::TaskPriority::High, &wg);

The tasks get added to the queue, and other threads (or the current one, when it is finished with the current task) can start executing them when they get popped off the queue.

AddTasks can optionally take a pointer to a WaitGroup. If you do, the value of the WaitGroup will incremented by the number of tasks queued. Every time a task finishes, the WaitGroup will be atomically decremented. You can use this functionality to create depencendies between tasks. You do that with the function

void WaitGroup::Wait();

This is where fibers come into play. If the value of WaitGroup == 0, the function trivially returns. If not, the scheduler will move the current fiber into a list of waiting fibers in the WaitGroup and grab a new fiber from the Fiber Pool. The new fiber pops a task from the Task Queue and starts execution with that.

But what about the task/fiber we stored in the WaitGroup? When will it finish being executed?

When the WaitGroup value hits zero from decrements, we add all the waiting fibers back into the queue in the TaskScheduler. The next time a thread switches fibers (either because the current fiber finished, or because it called WaitGroup::Wait() ), the ready Task will be picked up and resumed where it left off.


Advanced Features

Fibtex

Generally, you shouldn’t use Mutexes in fiber code, for two reasons:

  1. If you take a mutex, and call WaitGroup::Wait(), when Wait() resumes, your code could be on another thread. The mutex unlock will be undefined behavior, and probably lead to a deadlock

  2. Mutex contention will block the worker threads. And since we generally don’t oversubscribe the threads to the cores, this leaves cores idle.

To solve this, we created Fibtex. It implements the std lockable interface, so you can use it with all your favorite wrappers (std::lock_guard, std::unique_lock, etc.) It’s implemented behind the scenes with fiber waits, so if a Fibtex is locked, a waiter can switch to another task and do valuable work

Thread Pinning

When a fiber is resumed after a WaitGroup::Wait() or a Fibtex::lock(), there is no guarantee that it will resume on the same thread that it was running on when it was suspended. For most code, this is fine. However, certain libraries have strong assumptions. For example, in DirectX, you must do the final frame submit from the same thread that created the swap chain. Thus, some code will need to guarantee that fibers are resumed on the same thread where they were running when suspended. To do this, you can use the argument pinToCurrentThread. When set to true, the scheduler will guarantee that the resumed fiber will run on the same thread. This argument is available for WaitGroup::Wait() and Fibtext::lock(). NOTE: thread pinning is more expensive than the default behavior, and can potentially result in much slower resumption of the task in question (since it requires the pinned thread to finish the task it’s currently running). Therefore, it should only be used if truely necessary.


Dependencies

  • C++11 Compiler

  • CMake 3.2 or greater


Supported Platforms

Arch

Windows

Linux

OS X

iOS

Android

arm

Needs testing

Fully supported

In theory

In theory

In theory

arm_64

Needs testing

Fully supported

Needs testing

In theory

In theory

x86

Fully supported

Needs testing

Needs testing

x86_64

Fully supported

Fully supported

Fully supported


Building

FiberTaskingLib is a standard CMake build. However, for detailed instructions on how to build and include the library in your own project, see the documentation page.


License

The library is licensed under the Apache 2.0 license. However, FiberTaskingLib distributes and uses code from other Open Source Projects that have their own licenses:


Contributing

Contributions are very welcome. See the contributing page for more details.


Request for Feedback

This implementation was something I created because I thought Christian’s presentation was really interesting and I wanted to explore it myself. The code is still a work in progress and I would love to hear your critiques of how I could make it better. I will continue to work on this project and improve it as best as possible.

fibertaskinglib's People

Contributors

adastleyatvi avatar brodyhiggerson avatar cwfitzgerald avatar darthgelum avatar henrywiechert avatar jklarowicz avatar martty avatar mdsitton avatar richiesams avatar ruby0x1 avatar turol avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fibertaskinglib's Issues

Add ability to pin tasks to a thread

For example, the windows message pump will only send messages to the thread that created the window. So if the mainTask gets migrated to a worker thread, messages will no longer arrive, and the window will stop receiving input.

The only way for a fiber to migrate threads is when it is waited on. Our idea is to add a 2 slot, per-thread array of "pinned" waiting tasks.

Then WaitForCounter would change to:

WaitFotCounter(Counter counter, uint value, bool pinToCurrentThread = false)

WaitFreeQueue::Pop and WaitFreeQueue::Steal possible issue of item-copy

Please note that these two will always make a copy of the item into T *value provided, even when the race was lost:

*value = array->Get(b);
*value = array->Get(t);

As far as I can tell this is safe in current implementation, with simple POD TaskBundle structure.
What I am worry about is that CircularArray::Get returns a copy of underlying item instead of thr item itself through reference/pointer.

Adding Multiple Tasks on a Counter only Increments Counter Once

The following code fails

scheduler->AddTask(task1, &counter);
scheduler->AddTask(task2, &counter);

scheduler->WaitForCounter(&counter, 0);

Will cause issues because the counter is set to one instead of being incremented by one on https://github.com/RichieSams/FiberTaskingLib/blob/master/source/task_scheduler.cpp#L331. Similar problem on https://github.com/RichieSams/FiberTaskingLib/blob/master/source/task_scheduler.cpp#L354 with AddTasks. I didn't submit a PR because it's an easy enough fix and I have some other abstractions on my branch.

Benchmark converting TaskScheduler::m_waitingTaskLock to a ReadWriteLock

Theoretically, the m_waitingTasks list should experience many more reads than writes. Since a write will only happen when we create a new waiting task or when one is ready to be popped off. Whereas, (in the current design), we read the entire list every time a fiber looks for a new task to execute.

We can reduce contention by using a ReadWrite lock instead just a simple lock. That said, if the task execution time is relatively long in comparison to the time that it takes to iterate through the list and do the comparisons, there will be very little contention to begin with. And perhaps the extra logic is not necessary.

So, convert TaskScheduler::m_waitingTaskLock to a ReadWriteLock, and then create a benchmark to test the differences between a normal lock and a ReadWriteLock. Perhaps we can use the ProcuderConsumer test, and vary the spin wait time to simulate difference task execution times.

OS X tests compiled and running

I managed to get OS X running and compiling quite easily, so figured I would post the information here for discussion, before potentially opening a PR.

Setup

I used homebrew for dependencies.
make sure you update homebrew for boost >=1.58 i.e run brew update first

Started with cmake:
brew install cmake or brew upgrade cmake

Then boost:
brew install boost

Build errors/fixes

Since there are concepts missing and compiler errors the following changes were needed to get started. Note that obviously the ideal is parity, but starting somewhere is good.

/source/fiber_tasking_lib/thread_abstraction.h

ThreadId definition

#if defined(__linux__)
        typedef pid_t ThreadId;
#elif defined(__APPLE__)
       typedef mach_port_t ThreadId;
 #endif

ThreadId function:

Note that the resulting ID returned is a uint64 ID, but the code in task_scheduler seems to be presuming they are id's from 0 and are used into an [] index. Unless I misread, but this probably needs to be looked at.

#if defined(__linux__)
    inline ThreadId FTLGetCurrentThreadId() {
        return syscall(SYS_gettid);
    }
#elif defined(__APPLE__)
    inline ThreadId FTLGetCurrentThreadId() {
        return pthread_mach_thread_np(pthread_self());
    }
#else
    #error Implement FTLGetCurrentThreadId for this platform
#endif

eastl_defaults.cpp (2 places)

osx malloc is in stdlib.h

#include <cstdio>
#ifdef __APPLE__
#include <stdlib.h>
#else
#include <malloc.h>
#endif

memory.h

std::align error

source/fiber_tasking_lib/tagged_heap_backed_linear_allocator.cpp:133:20: error: call to 'align' is ambiguous
        while ((userPtr = std::align(alignment, n, start, bufferSize)) == nullptr) {

Skipped custom align by #ifndef __APPLE__ around it.
This could be a problem, but just hiding the custom definition appeared to work fine.
(There is also a int posix_memalign(void **, size_t, size_t); in stdlib.h which may be of use somewhere here).

Thread Affinity

This one is more nuanced, so the simplest path was just NOP them for now.

Here is a great post on mac-wrapped parity functions that can be dropped in, with accompanying code that handles pthread but not pthread_attr set affinity easily. Here is the documentation for the actual affinity API.

I'll take a look closer at the affinity now that everyone is running.

Short stop fixes:

  • FTLCreateThread

      #ifndef __APPLE__
          // Set core affinity
          cpu_set_t cpuSet;
          CPU_ZERO(&cpuSet);
          CPU_SET(coreAffinity, &cpuSet);
          pthread_attr_setaffinity_np(&threadAttr, sizeof(cpu_set_t), &cpuSet);
      #endif
    
  • FTLSetCurrentThreadAffinity

inline void FTLSetCurrentThreadAffinity(size_t coreAffinity) {
    #ifndef __APPLE__
        cpu_set_t cpuSet;
        CPU_ZERO(&cpuSet);
        CPU_SET(coreAffinity, &cpuSet);

        pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuSet);
    #endif
}

Runtime error/fixes

From both the example and the tests, the same assertion:

[==========] Running 7 tests from 3 test cases.
[----------] Global test environment set-up.
[----------] 3 tests from FunctionalTests
[ RUN      ] FunctionalTests.CalcTriangleNum
Assertion failed: (traits_type::minimum_size() <= size_), function basic_fixedsize_stack, file /usr/local/include/boost/context/fixedsize_stack.hpp, line 42.
Abort trap: 6

fixed by increasing the stack size in task_scheduler.cpp

I do wonder if this is not a similar cause for problems on the failed tests mentioned in other issues?

It appears the minimum it wants without configuration is 2^17, I just tagged in a *x until it worked.

globalArgs->g_taskScheduler.m_fiberSwitchingFibers[FTLGetCurrentThreadId()] = FTLCreateFiber(32768*4,

There were 4 of these changes - so it should likely be a config value potentially passed in the initialize constructor or in a define value somewhere to allow flexibility.

Results

screen 2015-08-28 at 3 26 12 pm

I can do a PR for various changes but think there are probably things to solve in a more unified way - like a no affinity flag to aid in initial portability and things like that.

Open to thoughts!

Interest in Additional LIbrary Features

There are a couple of features that I am going to be developing for my personal project on a fork of this library, and I want to know if you would be interested in me upstreaming any/all of the features I'm planning on making. I'm not quite sure what you want the scope of this project to be. All of these would add on to the functionality, not replace it.

  • A type safe interface built on std::function.
  • A shared mutex.
  • A separate threadpool for IO blocking operations with api to call it.
  • Optional instrumentation of the fiber pool.

Create tests for thread / fiber abstractions

Boost.Context isn't behaving properly. I'm getting undefined behavior leading to a segfault when running the current set of tests. The SegFault happens after the switch to the waitingTask fiber. For some reason, the arg comes in as null (which causes a segfault since the arg is hard casted to a TaskManager instance)

I'm hoping a set of well structured tests might be able to tease out the culprit

WaitingList implementation

Hi

I also made an implementation of this fiber tasking system at work (but unfortunately, it's not opensource). Anyway, thanks for your work, it helped me to avoid a lot of mistakes!

I ended up with a different waiting list implementation which I think requires shorter locking (or no locking at all depending on how it's done). I'll try to explain it here, in case it helps.

In your implem, in the FiberStart loop, every worker thread checks the counter of every waiting task. But in theory, you only need to check the tasks that wait on a counter when you decrement this particular counter.

To do that, you need to store a list a waiting tasks per counter instead of having a global one. Let's say the counter becomes a struct that contains both its value, and a list a of waiting tasks, you can, every time you decrement it, check the list of waiting tasks and move the one that need to be woken up to a "wake up" list. Then at the beginning of the FiberStart loop, you only need to try to dequeue from this list.

Segmentation fault in test for Calculate Triangle Number on Mac OS X (Sierra)

I can prevent (or hide) the segmentation fault by moving the code that deletes the task array in TriangleNumberMainTask() so that the delete occurs after the WaitForCounter() call.

This does not make sense to me as it seems we should be able to delete the array immediately after calling AddTasks().

This occurs with commit [dd05035] using make (not Xcode). I will test on Windows 10 momentarily.

EDIT: This does not occur for me using VC2015 (amd_x64) in Release and Debug builds. Just an OS X issue?

Decide upon a naming standard and normalize the project

Currently there are a couple different naming standards going on:

FunctionNames

  • FiberTaskingLib::FTLUpperCamelCase
    • I created this style because there were clashes with the global namespace when inside FiberTaskingLib
    • For example: When coding inside FiberTaskingLib and you call CreateFiber(), it's ambiguous between the Win32 CreateFiber() and ours.
    • The workaround is to use explicit calling such as FiberTaskingLib::CreateFiber()
  • FiberTaskingLib::UpperCamelCase

#define

  • UPPER_UNDERSCORES
  • FTL_UPPER_UNDERSCORES

All the other naming standards are consistent

Boost-Context based builds are broken

Functional tests all seg-fault. The unit tests have managed to fix a number of other erros. However, this issue still remains.

Issue #13 shows that the seg-faults go away if we execute the tests in a specific order. This could mean that the shutdown code is missing something

Test if fibers can finish on the thread they didn't start on

Back when we started, the thread would barf if it didn't finish with the same fiber it started with. I believe the new Boost asm files fix this.

If this is the case, we can remove the need for the MainTask, and can do everything from the main thread. This would enable much nicer usage of the library.

Crash on Thread Join on Ubuntu 16.04 and 18.04

Alright, so this is a bit of a heisenbug. I put the test code in this repo so that you can test it (should compile on all desktop platforms fine).

On Ubuntu 16.04 and 18.04 on both gcc-5 and clang-6.0 there is a crash somewhere within jump_fcontext. The asm says the segfault is on the first line. pthread_join is always on the callstack on the main thread. This manifests as a segmentation fault if you are running it under gdb, but just causes the program to spin indefinitely if run outside of the debugger.

There's unfortunately not much more information I can provide. You can run the preprocessor on it to remove all the preprocessor magic, and if it helpful I can provide a cpp file with the preprocessesing of the crazy macros already done. However, I highly doubt it's a macro problem, as it is just doing what you would normally do without macros.

If you need any more information, let me know. Thanks for this lovely library

Add CONTRIBUTING.md

Contributions are welcome
Issues are welcome
For coding style, we should resolve Issue #23, and then create a style guide document. Then we can just link to that from here.

Look into what it would take to be malloc-free

The major issue will be the std::atomic_uint 'counter' that we use for dependencies. I can think of two options:

  1. Ask the user to allocate the counter, and pass it into AddTasks()
    • This could default to nullptr, which would disable the decrement as tasks finish
    • This relies heavily on the user keeping the counter valid. IE. if the counter leaves the stack scope
  2. Keep a pool of counters
    • This is what sewing does
    • The downside I see here, is that we have to find a time to "return" the counters back to the pool
      • Depending on how this is implemented, this could force us to narrow dependencies to only use counters returned from AddTasks
      • Or we could add a GetCounter()-like function, which would give the user a free counter from the pool, that they can use however they wish. And it can be used for dependencies

Another issues could be alignment requirements

Heavy synchronization activity

Hi there,

I've been trying different approaches to speed up my nasty N^2 algorithm in the core of my engine/server. Without any threading: ~44ms, with OpenMP: ~14.5ms, IntelTBB: ~8ms, FiberTaskingLib: ~14ms.

I did a bit of profiling of this with VTune, and found that a significant portion of time was spent in synchronization.

image

image

What are your thoughts on this?

task affinity and the main task

I was wondering on how you'd imaging a game engine to use this.

You have the concept of a Main Task which seems to land on arbitrary threads, just like all other task. Can you explain your thoughts on that?

I tried to init GLFW once inside that main task and that caused it to crash, even in moments when it was on the main thread. Now I probably don't want to do system calls inside tasks that run on fibers. However, from using the library it is not clear whether the context switching (register and stack) is robust enough.

  1. What CAN run inside these?
  2. can you outline a pseudo main game loop using this (with worst case, OpenGL in use).

Tests fail thread sanitizer.

Tried running the tests using the thread sanitizer and they failed in the calc triangles test.

However I don't know if these are false positives, or a genuine race condition.

  1. edited root CMakeLists.txt
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fsanitize=thread -fno-omit-frame-pointer")

  2. Edited TaskScheduler::Initialize to limit to two threads.
    m_numThreads = 2;//RAM: FTLGetNumHardwareThreads();

  3. created a 64 bit clang debug build (clang 3.6.2)
    CC=clang CXX=clang++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Debug"

  4. ran the test
    test/FiberTaskingLib-test

Find shown below the output from the thread sanitizer's first two conflicts:

WARNING: ThreadSanitizer: data race (pid=17941)
  Read of size 8 at 0x7d6c0001f640 by thread T2:
    #0 std::_Hashtable<int, std::pair<int const, FiberTaskingLib::Fiber*>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_bucket_index(int const&, unsigned long) const /usr/include/c++/5.2.0/bits/hashtable.h:621 (FiberTaskingLib-test+0x0000004f25ec)
    #1 std::__detail::_Map_base<int, std::pair<int const, FiberTaskingLib::Fiber*>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true>, true>::operator[](int&&) /usr/include/c++/5.2.0/bits/hashtable_policy.h:617 (FiberTaskingLib-test+0x0000004efd8a)
    #2 std::unordered_map<int, FiberTaskingLib::Fiber*, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> > >::operator[](int&&) /usr/include/c++/5.2.0/bits/unordered_map.h:672 (FiberTaskingLib-test+0x0000004edfd2)
    #3 FiberTaskingLib::TaskScheduler::ThreadStart(void*) ../source/fiber_tasking_lib/task_scheduler.cpp:38 (FiberTaskingLib-test+0x0000004eab98)

  Previous write of size 8 at 0x7d6c0001f640 by thread T1:
    [failed to restore the stack]

  Location is heap block of size 1688 at 0x7d6c0001f100 allocated by main thread:
    #0 operator new(unsigned long) /build/gcc-multilib/src/gcc-5.2.0/libsanitizer/tsan/tsan_interceptors.cc:571 (libtsan.so.0+0x000000025ef3)
    #1 FunctionalTests_CalcTriangleNum_Test::TestBody() ../test/calc_triangle_num.cpp:51 (FiberTaskingLib-test+0x00000048d14a)
    #2 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004cb4fa)
    #3 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c2ad0)
    #4 testing::Test::Run() ../libs/gtest/src/gtest.cc:2437 (FiberTaskingLib-test+0x0000004a0f19)
    #5 testing::TestInfo::Run() ../libs/gtest/src/gtest.cc:2612 (FiberTaskingLib-test+0x0000004a18fd)
    #6 testing::TestCase::Run() ../libs/gtest/src/gtest.cc:2730 (FiberTaskingLib-test+0x0000004a2212)
    #7 testing::internal::UnitTestImpl::RunAllTests() ../libs/gtest/src/gtest.cc:4604 (FiberTaskingLib-test+0x0000004aa5b1)
    #8 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004ccc71)
    #9 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c401e)
    #10 testing::UnitTest::Run() ../libs/gtest/src/gtest.cc:4222 (FiberTaskingLib-test+0x0000004a8c56)
    #11 RUN_ALL_TESTS() ../libs/gtest/include/gtest/gtest.h:2326 (FiberTaskingLib-test+0x00000048e752)
    #12 main ../test/main.cpp:16 (FiberTaskingLib-test+0x00000048e671)

  Thread T2 (tid=17944, running) created by main thread at:
    #0 pthread_create /build/gcc-multilib/src/gcc-5.2.0/libsanitizer/tsan/tsan_interceptors.cc:895 (libtsan.so.0+0x000000027a37)
    #1 FiberTaskingLib::FTLCreateThread(unsigned long*, unsigned int, void* (*)(void*), void*, unsigned long) ../source/fiber_tasking_lib/thread_abstraction.h:204 (FiberTaskingLib-test+0x0000004ec3a4)
    #2 FiberTaskingLib::TaskScheduler::Initialize(unsigned int, FiberTaskingLib::GlobalArgs*) ../source/fiber_tasking_lib/task_scheduler.cpp:186 (FiberTaskingLib-test+0x0000004eb841)
    #3 FunctionalTests_CalcTriangleNum_Test::TestBody() ../test/calc_triangle_num.cpp:52 (FiberTaskingLib-test+0x00000048d16e)
    #4 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004cb4fa)
    #5 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c2ad0)
    #6 testing::Test::Run() ../libs/gtest/src/gtest.cc:2437 (FiberTaskingLib-test+0x0000004a0f19)
    #7 testing::TestInfo::Run() ../libs/gtest/src/gtest.cc:2612 (FiberTaskingLib-test+0x0000004a18fd)
    #8 testing::TestCase::Run() ../libs/gtest/src/gtest.cc:2730 (FiberTaskingLib-test+0x0000004a2212)
    #9 testing::internal::UnitTestImpl::RunAllTests() ../libs/gtest/src/gtest.cc:4604 (FiberTaskingLib-test+0x0000004aa5b1)
    #10 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004ccc71)
    #11 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c401e)
    #12 testing::UnitTest::Run() ../libs/gtest/src/gtest.cc:4222 (FiberTaskingLib-test+0x0000004a8c56)
    #13 RUN_ALL_TESTS() ../libs/gtest/include/gtest/gtest.h:2326 (FiberTaskingLib-test+0x00000048e752)
    #14 main ../test/main.cpp:16 (FiberTaskingLib-test+0x00000048e671)

  Thread T1 (tid=17943, running) created by main thread at:
    #0 pthread_create /build/gcc-multilib/src/gcc-5.2.0/libsanitizer/tsan/tsan_interceptors.cc:895 (libtsan.so.0+0x000000027a37)
    #1 FiberTaskingLib::FTLCreateThread(unsigned long*, unsigned int, void* (*)(void*), void*, unsigned long) ../source/fiber_tasking_lib/thread_abstraction.h:204 (FiberTaskingLib-test+0x0000004ec3a4)
    #2 FiberTaskingLib::TaskScheduler::Initialize(unsigned int, FiberTaskingLib::GlobalArgs*) ../source/fiber_tasking_lib/task_scheduler.cpp:186 (FiberTaskingLib-test+0x0000004eb841)
    #3 FunctionalTests_CalcTriangleNum_Test::TestBody() ../test/calc_triangle_num.cpp:52 (FiberTaskingLib-test+0x00000048d16e)
    #4 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004cb4fa)
    #5 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c2ad0)
    #6 testing::Test::Run() ../libs/gtest/src/gtest.cc:2437 (FiberTaskingLib-test+0x0000004a0f19)
    #7 testing::TestInfo::Run() ../libs/gtest/src/gtest.cc:2612 (FiberTaskingLib-test+0x0000004a18fd)
    #8 testing::TestCase::Run() ../libs/gtest/src/gtest.cc:2730 (FiberTaskingLib-test+0x0000004a2212)
    #9 testing::internal::UnitTestImpl::RunAllTests() ../libs/gtest/src/gtest.cc:4604 (FiberTaskingLib-test+0x0000004aa5b1)
    #10 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004ccc71)
    #11 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c401e)
    #12 testing::UnitTest::Run() ../libs/gtest/src/gtest.cc:4222 (FiberTaskingLib-test+0x0000004a8c56)
    #13 RUN_ALL_TESTS() ../libs/gtest/include/gtest/gtest.h:2326 (FiberTaskingLib-test+0x00000048e752)
    #14 main ../test/main.cpp:16 (FiberTaskingLib-test+0x00000048e671)

SUMMARY: ThreadSanitizer: data race /usr/include/c++/5.2.0/bits/hashtable.h:621 std::_Hashtable<int, std::pair<int const, FiberTaskingLib::Fiber*>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_bucket_index(int const&, unsigned long) const
==================
==================
WARNING: ThreadSanitizer: data race (pid=17941)
  Read of size 8 at 0x7d6c0001f638 by thread T3:
    #0 std::_Hashtable<int, std::pair<int const, FiberTaskingLib::Fiber*>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_before_node(unsigned long, int const&, unsigned long) const <null> (FiberTaskingLib-test+0x0000004f5a72)
    #1 std::_Hashtable<int, std::pair<int const, FiberTaskingLib::Fiber*>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_node(unsigned long, int const&, unsigned long) const /usr/include/c++/5.2.0/bits/hashtable.h:632 (FiberTaskingLib-test+0x0000004f266c)
    #2 std::__detail::_Map_base<int, std::pair<int const, FiberTaskingLib::Fiber*>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true>, true>::operator[](int&&) /usr/include/c++/5.2.0/bits/hashtable_policy.h:618 (FiberTaskingLib-test+0x0000004efda6)
    #3 std::unordered_map<int, FiberTaskingLib::Fiber*, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> > >::operator[](int&&) /usr/include/c++/5.2.0/bits/unordered_map.h:672 (FiberTaskingLib-test+0x0000004edfd2)
    #4 FiberTaskingLib::TaskScheduler::ThreadStart(void*) ../source/fiber_tasking_lib/task_scheduler.cpp:38 (FiberTaskingLib-test+0x0000004eab98)

  Previous write of size 8 at 0x7d6c0001f638 by thread T1:
    [failed to restore the stack]

  Location is heap block of size 1688 at 0x7d6c0001f100 allocated by main thread:
    #0 operator new(unsigned long) /build/gcc-multilib/src/gcc-5.2.0/libsanitizer/tsan/tsan_interceptors.cc:571 (libtsan.so.0+0x000000025ef3)
    #1 FunctionalTests_CalcTriangleNum_Test::TestBody() ../test/calc_triangle_num.cpp:51 (FiberTaskingLib-test+0x00000048d14a)
    #2 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004cb4fa)
    #3 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c2ad0)
    #4 testing::Test::Run() ../libs/gtest/src/gtest.cc:2437 (FiberTaskingLib-test+0x0000004a0f19)
    #5 testing::TestInfo::Run() ../libs/gtest/src/gtest.cc:2612 (FiberTaskingLib-test+0x0000004a18fd)
    #6 testing::TestCase::Run() ../libs/gtest/src/gtest.cc:2730 (FiberTaskingLib-test+0x0000004a2212)
    #7 testing::internal::UnitTestImpl::RunAllTests() ../libs/gtest/src/gtest.cc:4604 (FiberTaskingLib-test+0x0000004aa5b1)
    #8 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004ccc71)
    #9 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c401e)
    #10 testing::UnitTest::Run() ../libs/gtest/src/gtest.cc:4222 (FiberTaskingLib-test+0x0000004a8c56)
    #11 RUN_ALL_TESTS() ../libs/gtest/include/gtest/gtest.h:2326 (FiberTaskingLib-test+0x00000048e752)
    #12 main ../test/main.cpp:16 (FiberTaskingLib-test+0x00000048e671)

  Thread T3 (tid=17945, running) created by main thread at:
    #0 pthread_create /build/gcc-multilib/src/gcc-5.2.0/libsanitizer/tsan/tsan_interceptors.cc:895 (libtsan.so.0+0x000000027a37)
    #1 FiberTaskingLib::FTLCreateThread(unsigned long*, unsigned int, void* (*)(void*), void*, unsigned long) ../source/fiber_tasking_lib/thread_abstraction.h:204 (FiberTaskingLib-test+0x0000004ec3a4)
    #2 FiberTaskingLib::TaskScheduler::Initialize(unsigned int, FiberTaskingLib::GlobalArgs*) ../source/fiber_tasking_lib/task_scheduler.cpp:186 (FiberTaskingLib-test+0x0000004eb841)
    #3 FunctionalTests_CalcTriangleNum_Test::TestBody() ../test/calc_triangle_num.cpp:52 (FiberTaskingLib-test+0x00000048d16e)
    #4 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004cb4fa)
    #5 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c2ad0)
    #6 testing::Test::Run() ../libs/gtest/src/gtest.cc:2437 (FiberTaskingLib-test+0x0000004a0f19)
    #7 testing::TestInfo::Run() ../libs/gtest/src/gtest.cc:2612 (FiberTaskingLib-test+0x0000004a18fd)
    #8 testing::TestCase::Run() ../libs/gtest/src/gtest.cc:2730 (FiberTaskingLib-test+0x0000004a2212)
    #9 testing::internal::UnitTestImpl::RunAllTests() ../libs/gtest/src/gtest.cc:4604 (FiberTaskingLib-test+0x0000004aa5b1)
    #10 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004ccc71)
    #11 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c401e)
    #12 testing::UnitTest::Run() ../libs/gtest/src/gtest.cc:4222 (FiberTaskingLib-test+0x0000004a8c56)
    #13 RUN_ALL_TESTS() ../libs/gtest/include/gtest/gtest.h:2326 (FiberTaskingLib-test+0x00000048e752)
    #14 main ../test/main.cpp:16 (FiberTaskingLib-test+0x00000048e671)

  Thread T1 (tid=17943, running) created by main thread at:
    #0 pthread_create /build/gcc-multilib/src/gcc-5.2.0/libsanitizer/tsan/tsan_interceptors.cc:895 (libtsan.so.0+0x000000027a37)
    #1 FiberTaskingLib::FTLCreateThread(unsigned long*, unsigned int, void* (*)(void*), void*, unsigned long) ../source/fiber_tasking_lib/thread_abstraction.h:204 (FiberTaskingLib-test+0x0000004ec3a4)
    #2 FiberTaskingLib::TaskScheduler::Initialize(unsigned int, FiberTaskingLib::GlobalArgs*) ../source/fiber_tasking_lib/task_scheduler.cpp:186 (FiberTaskingLib-test+0x0000004eb841)
    #3 FunctionalTests_CalcTriangleNum_Test::TestBody() ../test/calc_triangle_num.cpp:52 (FiberTaskingLib-test+0x00000048d16e)
    #4 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004cb4fa)
    #5 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c2ad0)
    #6 testing::Test::Run() ../libs/gtest/src/gtest.cc:2437 (FiberTaskingLib-test+0x0000004a0f19)
    #7 testing::TestInfo::Run() ../libs/gtest/src/gtest.cc:2612 (FiberTaskingLib-test+0x0000004a18fd)
    #8 testing::TestCase::Run() ../libs/gtest/src/gtest.cc:2730 (FiberTaskingLib-test+0x0000004a2212)
    #9 testing::internal::UnitTestImpl::RunAllTests() ../libs/gtest/src/gtest.cc:4604 (FiberTaskingLib-test+0x0000004aa5b1)
    #10 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004ccc71)
    #11 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) <null> (FiberTaskingLib-test+0x0000004c401e)
    #12 testing::UnitTest::Run() ../libs/gtest/src/gtest.cc:4222 (FiberTaskingLib-test+0x0000004a8c56)
    #13 RUN_ALL_TESTS() ../libs/gtest/include/gtest/gtest.h:2326 (FiberTaskingLib-test+0x00000048e752)
    #14 main ../test/main.cpp:16 (FiberTaskingLib-test+0x00000048e671)

SUMMARY: ThreadSanitizer: data race ??:0 std::_Hashtable<int, std::pair<int const, FiberTaskingLib::Fiber*>, std::allocator<std::pair<int const, FiberTaskingLib::Fiber*> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_before_node(unsigned long, int const&, unsigned long) const
==================

EmptyQueueBehavior::Sleep on Mac OS X never wakes

Since I'm running a game main loop which creates tasks that update game engine systems, if the loop is constrained by vsync then there will be gaps where no tasks exist. Because of this I wanted to define the empty queue behavior to either sleep the threads OTHER than the thread processing the main loop until the main loop creates more tasks or yield to the os. Using the new_empty_queue_behavior branch.

Using Mac OS X, EmptyQueueBehavior::Yield doesn't seem to effect thread usage at all, all threads remain at 100% except the one running the main loop which sleeps a bit after swapping the back buffer if vsync is enabled. EmptyQueueBehavior::Sleep seems to halt the program entirely. It doesn't even make it through one full iteration of the main loop. I suspect the condition variable is never waking the threads back up and when the main task does taskScheduler->WaitForCounter(...); it just halts forever and never returns.

AtomicCounter::WaitingFiberBundle ctor PinnedThreadIndexctor possibly wrong

I have noticed a cosmetic difference between AddFiberToWaitingList default value for pinnedThreadIndex set to std::numeric_limits<std::size_t>::max(), here:

bool AddFiberToWaitingList(std::size_t fiberIndex, uint targetValue, std::atomic<bool> *fiberStoredFlag, std::size_t pinnedThreadIndex = std::numeric_limits<std::size_t>::max());

...and AtomicCounter::WaitingFiberBundle ctor set as PinnedThreadIndex(0), here:

PinnedThreadIndex(0) {

I cannot point out if this is necessary (since AddFiberToWaitingList default will make it right) but IMO ctor should set it to an invalid value also, i.e.: std::numeric_limits<std::size_t>::max()

Update LICENSE

You need to update your LICENSE file to have your name and date at the bottom:

Copyright {yyyy} {name of copyright owner}

wrong indexing

m_taskScheduler->AddReadyFiber(m_waitingFibers[i].PinnedThreadIndex, m_waitingFibers[i].FiberIndex, m_waitingFibers[i].FiberStoredFlag);

Hey,

I think the indexing is broken. It should be:
m_waitingFibers[readyFiberIndices[i]].PinnedThreadIndex...

BR
Moritz

CC and CXX do not export to global scope in CI build scripts

Doing 'export' inside a bash file will only export to the scope of the file. It will not export globally.

We can find a way to have it export globally, or just move the export phrases to the travis.yml. I would prefer to do it all within the script, so the version is only in one place, but it may be more trouble than it's worth.

GetCurrentThread() uses constant pseudo handle.

Claim: Main thread handle (on Windows) will always point to the current thread, instead of the main one.

Please note that ::GetCurrentThread() returns constant pseudo handle (value: DWORD - 1):
https://github.com/RichieSams/FiberTaskingLib/blob/master/include/ftl/thread_abstraction.h#L133

This is wrongly stored as a handle for the main thread:
https://github.com/RichieSams/FiberTaskingLib/blob/master/source/task_scheduler.cpp#L275

::GetCurrentThread() details:
https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getcurrentthread

To correct this you have to create the handle with ::DuplicateHandle, and then close it with ::CloseHandle
Something like this:
::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(), ::GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS);

Google tests are not independent.

Hi,

I tried running test/FiberTaskingLib-test and it segfaulted :-(

> test/FiberTaskingLib-test
[==========] Running 7 tests from 3 test cases.
[----------] Global test environment set-up.
[----------] 3 tests from FunctionalTests
[ RUN      ] FunctionalTests.CalcTriangleNum
fish: “test/FiberTaskingLib-test” terminated by signal SIGSEGV (Address boundary error)

However if I ran the test as test/FiberTaskingLib-test --gtest_shuffle then it worked fine.

> test/FiberTaskingLib-test --gtest_shuffle
Note: Randomizing tests' orders with a seed of 1607 .
[==========] Running 7 tests from 3 test cases.
[----------] Global test environment set-up.
[----------] 2 tests from TLSAbstraction
[ RUN      ] TLSAbstraction.GetSet
[       OK ] TLSAbstraction.GetSet (9 ms)
[ RUN      ] TLSAbstraction.MultipleThreadGetSet
[       OK ] TLSAbstraction.MultipleThreadGetSet (227 ms)
[----------] 2 tests from TLSAbstraction (236 ms total)

[----------] 2 tests from FiberAbstraction
[ RUN      ] FiberAbstraction.SingleFiberSwitch
[       OK ] FiberAbstraction.SingleFiberSwitch (0 ms)
[ RUN      ] FiberAbstraction.NestedFiberSwitch
[       OK ] FiberAbstraction.NestedFiberSwitch (0 ms)
[----------] 2 tests from FiberAbstraction (1 ms total)

[----------] 3 tests from FunctionalTests
[ RUN      ] FunctionalTests.ProducerConsumer
[       OK ] FunctionalTests.ProducerConsumer (1245 ms)
[ RUN      ] FunctionalTests.Maze10x10
[       OK ] FunctionalTests.Maze10x10 (8 ms)
[ RUN      ] FunctionalTests.CalcTriangleNum
[       OK ] FunctionalTests.CalcTriangleNum (118 ms)
[----------] 3 tests from FunctionalTests (1371 ms total)

[----------] Global test environment tear-down
[==========] 7 tests from 3 test cases ran. (1608 ms total)
[  PASSED  ] 7 tests.

So I don't know what's happening, but it seems that there is some sort of setup that's not been cleared between tests which makes the triangle test work, where it would segfault before.

Notes:
Running on arch linux
Linux quaff 4.1.3-1-ARCH #1 SMP PREEMPT Wed Jul 22 20:37:12 CEST 2015 x86_64 GNU/Linux
I built boost 1.58 from source and linked it in
Segfault was in munmap (no call stack) at the retq instruction:

0x7ffff6e56ae0                   b8 0b 00 00 00        mov    $0xb,%eax
0x7ffff6e56ae5  <+0x0005>        0f 05                 syscall
0x7ffff6e56ae7  <+0x0007>        48 3d 01 f0 ff ff     cmp    $0xfffffffffffff001,%rax
0x7ffff6e56aed  <+0x000d>        73 01                 jae    0x7ffff6e56af0 <munmap+16>
0x7ffff6e56aef  <+0x000f>        c3                    retq
0x7ffff6e56af0  <+0x0010>        48 8b 0d 61 83 2b 00  mov    0x2b8361(%rip),%rcx        # 0x7ffff710ee58
0x7ffff6e56af7  <+0x0017>        f7 d8                 neg    %eax
0x7ffff6e56af9  <+0x0019>        64 89 01              mov    %eax,%fs:(%rcx)
0x7ffff6e56afc  <+0x001c>        48 83 c8 ff           or     $0xffffffffffffffff,%rax
0x7ffff6e56b00  <+0x0020>        c3                    retq

I limited cores to 2 and still reproduced the error.
The other thread was in:

static inline int
__gthread_yield (void)
{
  return __gthrw_(sched_yield) ();   // <-- thread was here
}

other thread's call stack

0   sched_yield         0x7ffff6e43f47  
1   __gthread_yield gthr-default.h  692 0x4cc9f5    
2   std::this_thread::yield thread  270 0x4ce245    
3   FiberTaskingLib::TaskScheduler::Quit    task_scheduler.cpp  259 0x4cd9d9    
4   FunctionalTests_CalcTriangleNum_Test::TestBody  calc_triangle_num.cpp   98  0x48cd60    
5   testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>  gtest.cc    2364    0x4b9868    
6   testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void> gtest.cc    2400    0x4b3f0e    
7   testing::Test::Run  gtest.cc    2437    0x49aa60    
8   testing::TestInfo::Run  gtest.cc    2612    0x49b1d4    
9   testing::TestCase::Run  gtest.cc    2730    0x49b7f3    
10  testing::internal::UnitTestImpl::RunAllTests    gtest.cc    4604    0x4a1f09    
11  testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>    gtest.cc    2364    0x4ba6e7    
12  testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>   gtest.cc    2400    0x4b4c5e    
13  testing::UnitTest::Run  gtest.cc    4222    0x4a0c6f    
14  RUN_ALL_TESTS   gtest.h 2326    0x48d8d7    
15  main    main.cpp    16  0x48d871    

Add ability to use system boost

Hi!

I tried to compile your library with the latest boost 1.63.0 but it does not work out of the box.
Is it possible to provide an alternate way of including boost except that from third party dir?

  1. Boost 1.63.0 does not have fcontext.h
  2. Namespace boost_context is not the default namespace for boost. Should be boost::context.

sources from boost.context

Please do not re-license code from Boost under Apache 2.0!
I suggest you move the Boost code to the folder 'third_party' as you did before.

Clean up #defines needed for portability

It's ugly to see the #if defined(BOOST_CONTEXT) everywhere. It's also not clear what is being tested for, as explained here

  • Create specific #defines rather than relying solely on _MSC_VER, etc.
    • CAN_USE_TLS
    • etc.
    • Create a config.h where all these are defined

Add cmpxchg to AtomicCounter to Enable Mutexes

In order to build a fiber based mutex AtomicCounter needs either cmpxchg. As for what to do with resuming the fibers, I think if the cmpxchg returns true, the fibers with the new value should be run.

Add units tests to check that the Floating Point registers are being properly saved

Previous versions of the asm files ignored the FP registers. So a fiber switch could corrupt the data left in the registers.

We should create a set of unit tests to check that this does not happen. I'm thinking something similar to the current SingleFiberSwitch and NestedFiberSwitch, but using floating point numbers instead of integers.

The hard part will be dealing with floating point rounding errors.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.