Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
dougbinks committed Oct 28, 2019
2 parents 346f847 + ba0ded5 commit fb1151f
Show file tree
Hide file tree
Showing 14 changed files with 740 additions and 193 deletions.
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ if( ENKITS_BUILD_EXAMPLES )
add_executable( TestWaitforTask example/TestWaitforTask.cpp )
target_link_libraries(TestWaitforTask enkiTS )

add_executable( ExternalTaskThread example/ExternalTaskThread.cpp )
target_link_libraries(ExternalTaskThread enkiTS )

if( ENKITS_BUILD_C_INTERFACE )
add_executable( ParallelSum_c example/ParallelSum_c.c )
target_link_libraries(ParallelSum_c enkiTS )
Expand All @@ -106,5 +109,9 @@ if( ENKITS_BUILD_C_INTERFACE )
add_executable( Priorities_c example/Priorities_c.c )
target_link_libraries(Priorities_c enkiTS )
endif()

add_executable( ExternalTaskThread_c example/ExternalTaskThread_c.c )
target_link_libraries(ExternalTaskThread_c enkiTS )

endif()
endif()
106 changes: 81 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,39 @@ For cmake, on Windows / Mac OS X / Linux with cmake installed, open a prompt in
1. *Up-front Allocation friendly* - enkiTS is designed for zero allocations during scheduling.
1. *Can pin tasks to a given thread* - enkiTS can schedule a task which will only be run on the specified thread.
1. *Can set task priorities* - Up to 5 task priorities can be configured via define ENKITS_TASK_PRIORITIES_NUM (defaults to 3). Higher priority tasks are run before lower priority ones.
1. **NEW** *Can register external threads to use with enkiTS* - Can configure enkiTS with numExternalTaskThreads which can be registered to use with the enkiTS API.

## Usage

C++ usage:
- full example in [example/ParallelSum.cpp](example/ParallelSum.cpp)
- C example in [example/ParallelSum_c.c](example/ParallelSum_c.c)
```C
#include "TaskScheduler.h"

enki::TaskScheduler g_TS;

// define a task set, can ignore range if we only do one thing
struct ParallelTaskSet : enki::ITaskSet {
virtual void ExecuteRange( enki::TaskSetPartition range, uint32_t threadnum ) {
// do something here, can issue tasks with g_TS
}
virtual void ExecuteRange( enki::TaskSetPartition range, uint32_t threadnum ) {
// do something here, can issue tasks with g_TS
}
};

int main(int argc, const char * argv[]) {
g_TS.Initialize();
ParallelTaskSet task; // default constructor has a set size of 1
g_TS.AddTaskSetToPipe( &task );
g_TS.Initialize();
ParallelTaskSet task; // default constructor has a set size of 1
g_TS.AddTaskSetToPipe( &task );

// wait for task set (running tasks if they exist) - since we've just added it and it has no range we'll likely run it.
g_TS.WaitforTask( &task );
return 0;
// wait for task set (running tasks if they exist)
since we've just added it and it has no range we'll likely run it.
g_TS.WaitforTask( &task );
return 0;
}
```
C++ 11 lambda usage:
- full example in [example/LambdaTask.cpp](example/LambdaTask.cpp)
```C
#include "TaskScheduler.h"
Expand All @@ -91,7 +96,9 @@ int main(int argc, const char * argv[]) {
}
```

Task priorities usage in C++ (see example/Priorities_c.c for C example).
Task priorities usage in C++:
- full example in [example/Priorities.cpp](example/Priorities.cpp)
- C example in [example/Priorities_c.c](example/Priorities_c.c)
```C
// See full example in Priorities.cpp
#include "TaskScheduler.h"
Expand Down Expand Up @@ -136,7 +143,9 @@ int main(int argc, const char * argv[])
}
```
Pinned Tasks usage in C++ (see example/PinnedTask_c.c for C example).
Pinned Tasks usage in C++:
- full example in [example/PinnedTask.cpp](example/PinnedTask.cpp)
- C example in [example/PinnedTask_c.c](example/PinnedTask_c.c)
```C
#include "TaskScheduler.h"
Expand All @@ -146,23 +155,68 @@ enki::TaskScheduler g_TS;
struct PinnedTask : enki::IPinnedTask {
virtual void Execute() {
// do something here, can issue tasks with g_TS
}
}
};
int main(int argc, const char * argv[]) {
g_TS.Initialize();
PinnedTask task; //default constructor sets thread for pinned task to 0 (main thread)
g_TS.AddPinnedTask( &task );
// RunPinnedTasks must be called on main thread to run any pinned tasks for that thread.
// Tasking threads automatically do this in their task loop.
g_TS.RunPinnedTasks();
// wait for task set (running tasks if they exist) - since we've just added it and it has no range we'll likely run it.
g_TS.WaitforTask( &task );
return 0;
g_TS.Initialize();
PinnedTask task; //default constructor sets thread for pinned task to 0 (main thread)
g_TS.AddPinnedTask( &task );
// RunPinnedTasks must be called on main thread to run any pinned tasks for that thread.
// Tasking threads automatically do this in their task loop.
g_TS.RunPinnedTasks();
// wait for task set (running tasks if they exist)
// since we've just added it and it has no range we'll likely run it.
g_TS.WaitforTask( &task );
return 0;
}
```

External thread usage in C++:
- full example in [example/ExternalTaskThread.cpp](example/ExternalTaskThread.cpp)
- C example in [example/ExternalTaskThread_c.c](example/ExternalTaskThread_c.c)
```C
#include "TaskScheduler.h"

enki::TaskScheduler g_TS;
struct ParallelTaskSet : ITaskSet
{
virtual void ExecuteRange( TaskSetPartition range, uint32_t threadnum )
{
// Do something
}
};

void threadFunction()
{
g_TS.RegisterExternalTaskThread();

// sleep for a while instead of doing something such as file IO
std::this_thread::sleep_for( std::chrono::milliseconds( num_ * 100 ) );

ParallelTaskSet task;
g_TS.AddTaskSetToPipe( &task );
g_TS.WaitforTask( &task);

g_TS.DeRegisterExternalTaskThread();
}

int main(int argc, const char * argv[])
{
enki::TaskSchedulerConfig config;
config.numExternalTaskThreads = 1; // we have one extra external thread

g_TS.Initialize( config );

std::thread exampleThread( threadFunction );

exampleThread.join();

return 0;
}
```
C usage:
```C
Expand All @@ -178,13 +232,14 @@ int main(int argc, const char * argv[]) {
enkiTaskSet* pTask;
g_pTS = enkiNewTaskScheduler();
enkiInitTaskScheduler( g_pTS );
// create a task, can re-use this to get allocation occurring on startup
pTask = enkiCreateTaskSet( g_pTS, ParalleTaskSetFunc );
pTask = enkiCreateTaskSet( g_pTS, ParalleTaskSetFunc );
enkiAddTaskSetToPipe( g_pTS, pTask, NULL, 1); // NULL args, setsize of 1
// wait for task set (running tasks if they exist) - since we've just added it and it has no range we'll likely run it.
// wait for task set (running tasks if they exist)
// since we've just added it and it has no range we'll likely run it.
enkiWaitForTaskSet( g_pTS, pTask );
enkiDeleteTaskSet( pTask );
Expand All @@ -195,6 +250,7 @@ int main(int argc, const char * argv[]) {
}
```


## Bindings

- C# [EnkiTasks C#](https://github.com/nxrighthere/EnkiTasks-CSharp)
Expand Down
95 changes: 95 additions & 0 deletions example/ExternalTaskThread.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2019 Doug Binks
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

#include "TaskScheduler.h"

#include <stdio.h>
#include <thread>

using namespace enki;

TaskScheduler g_TS;
static std::atomic<int32_t> g_Run;


struct ParallelTaskSet : ITaskSet
{
ParallelTaskSet() { m_SetSize = 100; }

virtual void ExecuteRange( TaskSetPartition range_, uint32_t threadnum_ )
{
printf(" Run %d: This could run on any thread, currently thread %d\n", g_Run.load(), threadnum_);

// sleep used as a 'pretend' workload
std::chrono::milliseconds sleepTime( range_.end - range_.start );
std::this_thread::sleep_for( sleepTime );
}
};

// Example thread function
// May want to use threads for blocking IO, during which enkiTS task threads can do work
void threadFunction( uint32_t num_ )
{
bool bRegistered = g_TS.RegisterExternalTaskThread();
assert( bRegistered );
if( bRegistered )
{
// sleep for a while instead of doing something such as file IO
std::this_thread::sleep_for( std::chrono::milliseconds( num_ * 100 ) );


ParallelTaskSet task;
g_TS.AddTaskSetToPipe( &task );
g_TS.WaitforTask( &task);
g_TS.DeRegisterExternalTaskThread();
}
}

static const int REPEATS = 5;
static const uint32_t NUMEXTERNALTHREADS = 5;

int main(int argc, const char * argv[])
{
enki::TaskSchedulerConfig config;
config.numExternalTaskThreads = NUMEXTERNALTHREADS;

std::thread threads[NUMEXTERNALTHREADS];
g_TS.Initialize( config );


for( g_Run = 0; g_Run< REPEATS; ++g_Run )
{
printf("Run %d\n", g_Run.load() );

for( uint32_t iThread = 0; iThread < NUMEXTERNALTHREADS; ++iThread )
{
threads[ iThread ] = std::thread( threadFunction, iThread );
}

// check that out of order Deregister / Register works...
threads[ 0 ].join();
threads[ 0 ] = std::thread( threadFunction, 0 );

for( uint32_t iThread = 0; iThread < NUMEXTERNALTHREADS; ++iThread )
{
threads[ iThread ].join();
}
}

return 0;
}
Loading

0 comments on commit fb1151f

Please sign in to comment.