/****************************************************************************
*
* Copyright (c) 2017, Michael Becker (michael.f.becker@gmail.com)
*
* This file is part of the FreeRTOS Add-ons project.
*
* Source Code:
* https://github.com/michaelbecker/freertos-addons
*
* Project Page:
* http://michaelbecker.github.io/freertos-addons/
*
* On-line Documentation:
* http://michaelbecker.github.io/freertos-addons/docs/html/index.html
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so,subject to the
* following conditions:
*
* + The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
* + Credit is appreciated, but not required, if you find this project
* useful enough to include in your application, product, device, etc.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
***************************************************************************/
#ifndef WORK_QUEUE_HPP_
#define WORK_QUEUE_HPP_
#include "thread.hpp"
#include "queue.hpp"
#include "semaphore.hpp"
namespace cpp_freertos {
#define DEFAULT_MAX_WORK_ITEMS 10
#define DEFAULT_WORK_QUEUE_STACK_SIZE (configMINIMAL_STACK_SIZE * 2)
#define DEFAULT_WORK_QUEUE_PRIORITY (tskIDLE_PRIORITY + 1)
/**
* This class encapsulates the idea of a discrete, non-repeating task.
* Create a WorkItem when there is something you need to do on a different
* Thread, but doesn't have to happen periodically. This is a great
* construct for one off fire and forget tasks.
*
* This is an abstract base class.
* To use this, you need to subclass it. All of your WorkItems should
* be derived from this class. Then implement the virtual Run
* function. This is a similar design to Java threading.
*/
class WorkItem {
/////////////////////////////////////////////////////////////////////////
//
// Public API
//
/////////////////////////////////////////////////////////////////////////
public:
/**
* Our constructor.
*
* @param freeAfterComplete If you pass in a true, you are
* requesing the WorkQueue itself to delete this WorkItem after
* it has run it.
* @note Only set freeAfterComplete = true if:
* 1) You dynamically allocated it (i.e. used "new")
* 2) After you call QueueWork() you promise never to touch
* this object again.
*/
WorkItem(bool freeAfterComplete = false);
/**
* Our destructor.
*/
virtual ~WorkItem();
/**
* Allows a client to decide if this WorkItem is marked
* for automatic deletion.
*/
bool FreeAfterRun();
/**
* Implementation of your actual WorkItem function.
* You must override this function.
*/
virtual void Run() = 0;
/////////////////////////////////////////////////////////////////////////
//
// Private API
// The internals of this wrapper class.
//
/////////////////////////////////////////////////////////////////////////
private:
/**
* Designates whether this WorkItem should be deleted
* after the WorkQueue has run it.
*/
const bool FreeItemAfterCompleted;
};
/**
* This class is the "engine" for WorkItems. Create one or more WorkQueues
* to accept WorkItems. WorkQueues pull WorkItems off of a FIFO queue and
* run them sequentially.
*/
class WorkQueue {
/////////////////////////////////////////////////////////////////////////
//
// Public API
//
/////////////////////////////////////////////////////////////////////////
public:
/**
* Constructor to create a named WorkQueue.
*
* @throws ThreadCreateException, QueueCreateException,
* SemaphoreCreateException
* @param Name Name of the thread internal to the WorkQueue.
* Only useful for debugging.
* @param StackDepth Number of "words" allocated for the Thread stack.
* @param Priority FreeRTOS priority of this Thread.
* @param MaxWorkItems Maximum number of WorkItems this WorkQueue can hold.
*/
WorkQueue( const char * const Name,
uint16_t StackDepth = DEFAULT_WORK_QUEUE_STACK_SIZE,
UBaseType_t Priority = DEFAULT_WORK_QUEUE_PRIORITY,
UBaseType_t MaxWorkItems = DEFAULT_MAX_WORK_ITEMS);
/**
* Constructor to create an unnamed WorkQueue.
*
* @throws ThreadCreateException, QueueCreateException,
* SemaphoreCreateException
* @param StackDepth Number of "words" allocated for the Thread stack.
* @param Priority FreeRTOS priority of this Thread.
* @param MaxWorkItems Maximum number of WorkItems this WorkQueue can hold.
*/
WorkQueue( uint16_t StackDepth = DEFAULT_WORK_QUEUE_STACK_SIZE,
UBaseType_t Priority = DEFAULT_WORK_QUEUE_PRIORITY,
UBaseType_t MaxWorkItems = DEFAULT_MAX_WORK_ITEMS);
#if (INCLUDE_vTaskDelete == 1)
/**
* Our destructor.
*
* @note Given the multithreaded nature of this class, the dtor
* may block until the underlying Thread has had a chance to
* clean up.
*/
~WorkQueue();
#else
//
// If we are using C++11 or later, take advantage of the
// newer features to find bugs.
//
#if __cplusplus >= 201103L
/**
* If we can't delete a task, it makes no sense to have a
* destructor.
*/
~WorkQueue() = delete;
#endif
#endif
/**
* Send a WorkItem off to be executed.
*
* @param work Pointer to a WorkItem.
* @return true if it was queued, false otherwise.
* @note This function may block if the WorkQueue is presently full.
*/
bool QueueWork(WorkItem *work);
/////////////////////////////////////////////////////////////////////////
//
// Private API
// The internals of this class.
//
/////////////////////////////////////////////////////////////////////////
private:
/**
* An internal derived Thread class, in which we do our real work.
*/
class CWorkerThread : public Thread {
public:
CWorkerThread( const char * const Name,
uint16_t StackDepth,
UBaseType_t Priority,
WorkQueue *Parent);
CWorkerThread( uint16_t StackDepth,
UBaseType_t Priority,
WorkQueue *Parent);
virtual ~CWorkerThread();
protected:
virtual void Run();
private:
const WorkQueue *ParentWorkQueue;
};
/**
* Pointer to our WorkerThread.
*/
CWorkerThread *WorkerThread;
/**
* Pointer to our work queue itself.
*/
Queue *WorkItemQueue;
/**
* Semaphore to support deconstruction without race conditions.
*/
BinarySemaphore *ThreadComplete;
};
}
#endif