coroutine 2


Goal: see coroutineYieldTest()
We get promise.mValue by calling hRange.resume().

How it’s achieved:
coroutine_handle hRange is from CReturnObjGen, which is the return from calling cofuncYield.
Coroutine cofuncYield() has a loop that keeps calling co_yield to return an integer.
Different from coroutine-1 in last post, CReturnObjGen has no base class.
It has to implement subclass promise_type.
The yield value promise.mValue is passed in by promise.yield_value(value)
CReturnObjGen holds the handle in mhCoroutine, which is passed in by ctor, which is somehow called.
cofuncYield(): upon each trigger, it passes co_yield and then loops back and block.
has no return statement even though it has return type.

Download: http://riowing.net/p/wp/coroutine.cpp

coroutine 1


Goal: see Test_generator
We get value by calling hReturnGen’s iterator, as a container like vector.
hReturnGen is the return when the coroutine is called.

How it’s achieved:
Coroutine cofunc_generator() has a loop that keeps calling co_yield to return an integer.
hReturnGen is based on experimental::generator, which does the heavy lifting.

A peek inside:
The iterator can come from :generator.begin()
iterator._Coro.promise()._Value holds the yield value
_Coro is the coroutine handle.

Download: http://riowing.net/p/wp/coroutine.cpp

Thread Pool


Thread pool implemented in CThreadPool, a pool of iPoolSize = 2 threads running all the time, taking task = worker function from mQueue

Why use pool:
to avoid cost of thread creation and destruction.
better tracking: like max number of threads.

How to use it: see TestThreadPool()
fuFunctor = myPool.Push(myFunctor, ‘b’); //’b’ is parameters of the functor/function
fuFunctor is the future, fuFunctor.get(), which verly likely calls .wait internally; gives return value and indicates the function is done.

APIs used: :vector of :thread-s, queue of tasks=function, packaged_task, :condition_variable, :mutex

Implementation:
How a task is added: see .Push()
caller passes in function ptr and parameters
bind() package them to form a packaged_task, which exposes a :future to be returned to caller.
after mQueue.emplace this this task, mCv.notify_one

How is return value set: when caller’s function calls return 2; 2 is tranfered to fuFunctor and cause fuFunctor.get to exit, all handled by packaged_task
to exit: return statement in func cause packaged_task to set future.
The workers: 2 instance of funcThread running, each: cv.wait, take one from mQueue and run the task.
ctor: fill pool by starting all threads

Notes: packaged_task is higher level than :thread as it automatically set future, while with :thread, we have to manually pass and set future.
result_of replaced by invoke_result

Download: http://riowing.net/p/wp/Threadpool.cpp

SubTypeExist


Check if CSubTypeYes has sub type CSubTypeYes::SubType, by calling a template, in two ways:

  1. by template class:
    bExist = CSubtypeExist<ClassToCheck>::value
  2. by template function:
    bExist = subtypeExistByReturnValue<ClassToCheck>(nullptr)

How it works:

  1. by class:
    template<class T, class U = size_t> struct CSubtypeExist { const static bool value = false; };
    is the base template, with U default to size_t.
    template<class T> struct CSubtypeExist< T, decltype(sizeof(T::SubType)) > { const static bool value = true; };
    partial specializes the base by indicating U to be the type of sizeof(SubType).
    when no T::SubType, this is SFINAE and fall back to base.
    sizeof is the key since there is no such thing as overloading for class.
  2. by function:
    template<class T> constexpr bool subtypeExistByReturnValue(…) { return false; }
    is the base template
    template<class T> constexpr bool subtypeExistByReturnValue(typename T::SubType*) { return true; }
    overloads the base by accepting function parameter of SubType pointer.
    typename is required here, not sure why.

Download: http://riowing.net/p/wp/SubType.cpp