idle: Add an idle queue

The idle queue is used to schedule tasks ... later.  This code was
mostly adoted from a reference TDD experiment by Josh Larson
(themutatedshrimp@gmail.com).  Thanks Josh!

Signed-off-by: Anna Schumaker <schumaker.anna@gmail.com>
This commit is contained in:
Anna Schumaker 2013-09-01 10:55:13 -04:00 committed by Anna Schumaker
parent 5f30a71c55
commit f417b48df8
10 changed files with 236 additions and 10 deletions

3
config
View File

@ -28,6 +28,7 @@ class Config:
self.FILE = False self.FILE = False
self.FILTER = False self.FILTER = False
self.GROUP = False self.GROUP = False
self.IDLE = False
self.INDEX = False self.INDEX = False
self.TEST = False self.TEST = False
@ -40,6 +41,7 @@ class Config:
if self.FILE: env.Append( CCFLAGS = [ "-DCONFIG_FILE" ]) if self.FILE: env.Append( CCFLAGS = [ "-DCONFIG_FILE" ])
if self.FILTER: env.Append( CCFLAGS = [ "-DCONFIG_FILTER" ]) if self.FILTER: env.Append( CCFLAGS = [ "-DCONFIG_FILTER" ])
if self.GROUP: env.Append( CCFLAGS = [ "-DCONFIG_GROUP" ]) if self.GROUP: env.Append( CCFLAGS = [ "-DCONFIG_GROUP" ])
if self.IDLE: env.Append( CCFLAGS = [ "-DCONFIG_IDLE" ])
if self.INDEX: env.Append( CCFLAGS = [ "-DCONFIG_INDEX" ]) if self.INDEX: env.Append( CCFLAGS = [ "-DCONFIG_INDEX" ])
if self.TEST: env.Append( CCFLAGS = [ "-DCONFIG_TEST" ]) if self.TEST: env.Append( CCFLAGS = [ "-DCONFIG_TEST" ])
@ -48,6 +50,7 @@ class Config:
self.FILE = False self.FILE = False
self.FILTER = False self.FILTER = False
self.GROUP = False self.GROUP = False
self.IDLE = False
self.INDEX = False self.INDEX = False
self.TEST = False self.TEST = False
self.reconfigure() self.reconfigure()

View File

@ -13,8 +13,17 @@ Idle queue: (lib/idle.cpp)
tasks must inherit from the IdleBase class so that multiple templated tasks must inherit from the IdleBase class so that multiple templated
IdleTask pointers can be placed on the same idle queue. IdleTask pointers can be placed on the same idle queue.
Creating an idle queue in idle.hpp will create a new queue for every
file that idle.h is included in. I want to have a single, shared
idle queue used by the entire application so to get around this the
IdleBase class is used and implemented in lib/idle.cpp.
- IdleBase: - IdleBase:
class IdleBase { class IdleBase {
private:
schedule();
public:
IdleBase(); IdleBase();
~IdleBase(); ~IdleBase();
virtual void run() = 0; virtual void run() = 0;
@ -24,30 +33,34 @@ Idle queue: (lib/idle.cpp)
template <class T> template <class T>
class IdleTask : IdleBase { class IdleTask : IdleBase {
private: private:
void (*func)(T *); void (*func)(T &);
T *data; T &data;
public: public:
IdleTask(void (*)(T *), T *); IdleTask(void (*)(T &), T);
void run(); void run();
}; };
- Queue: - Queue:
deque(IdleBase *> idle_queue; queue<IdleBase *> idle_queue;
float queued = 0.0 float queued = 0.0
float serviced = 0.0 float serviced = 0.0
- API: - API:
void IdleBase :: schedule();
Add the idle task to the idle queue. This should be called
by the IdleTask constructor.
queued++;
template <class T> template <class T>
void idle :: schedule(void (*)(T *), T *); static inline void idle :: schedule(void (*)(T &), T);
Schedule a function to run later (queued++). This should Create a new IdleTask to run the function later.
be written in the idle.hpp file since it is a function
template.
bool idle :: run_task() bool idle :: run_task()
If there are tasks on the queue: If there are tasks on the queue:
run the next task run the next task
scheduled++ scheduled++
If there are still tasks on the queue:
return true return true
else: else:
queued = 0 queued = 0
@ -56,4 +69,5 @@ Idle queue: (lib/idle.cpp)
float idle :: get_progress() float idle :: get_progress()
Return (serviced / queued) to the caller. If there are no Return (serviced / queued) to the caller. If there are no
tasks, return 1.0 to indicate that the queue is finished. tasks, return 1.0 to indicate that the queue is finished (and
to avoid a divide-by-zero error).

45
include/idle.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#ifndef OCARINA_IDLE_H
#define OCARINA_IDLE_H
namespace idle
{
class IdleBase {
protected:
void schedule();
public:
IdleBase();
virtual ~IdleBase();
virtual void run() = 0;
};
template <class T>
class IdleTask : public IdleBase {
private:
void (*func)(T &);
T data;
public:
IdleTask(void (*)(T &), T);
~IdleTask();
void run();
};
template <class T>
static inline void schedule(void (*func)(T &), T param)
{
new IdleTask<T>(func, param);
}
bool run_task();
float get_progress();
};
#include "idle.hpp"
#endif /* OCARINA_IDLE_H */

28
include/idle.hpp Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*
* DO NOT INCLUDE THIS FILE DIRECTLY. THIS IS A TEMPLATE DEFINITION FILE
* AND ONLY MEANT TO BE INCLUDED BY include/idle.h!
*/
#ifndef OCARINA_IDLE_HPP
#define OCARINA_IDLE_HPP
template <class T>
idle :: IdleTask<T> :: IdleTask(void (*fn)(T &), T param)
: func(fn), data(param)
{
IdleBase :: schedule();
}
template <class T>
idle :: IdleTask<T> :: ~IdleTask()
{
}
template <class T>
void idle :: IdleTask<T> :: run()
{
func(data);
}
#endif /* OCARINA_IDLE_HPP */

View File

@ -27,6 +27,10 @@ if CONFIG.FILE:
CONFIG.package("glib-2.0") CONFIG.package("glib-2.0")
build += [ env.Object("file.cpp") ] build += [ env.Object("file.cpp") ]
if CONFIG.IDLE:
build += [ env.Object("idle.cpp") ]
####################
if CONFIG.TEST: if CONFIG.TEST:
build += [ env.Object("test.cpp") ] build += [ env.Object("test.cpp") ]

49
lib/idle.cpp Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <idle.h>
#include <queue>
static std::queue<idle :: IdleBase *> idle_queue;
static float queued = 0;
static float serviced = 0;
idle :: IdleBase :: IdleBase()
{
}
idle :: IdleBase :: ~IdleBase()
{
}
void idle :: IdleBase :: schedule()
{
idle_queue.push(this);
queued++;
}
bool idle :: run_task()
{
if (idle_queue.size() > 0) {
idle_queue.front()->run();
delete idle_queue.front();
idle_queue.pop();
serviced++;
}
if (idle_queue.size() > 0)
return true;
queued = 0;
serviced = 0;
return false;
}
float idle :: get_progress()
{
if (idle_queue.size() == 0)
return 1;
return serviced / queued;
}

View File

@ -43,7 +43,7 @@ Export("Test")
# Read SConscript files # Read SConscript files
scripts = [ "basic", "database", "file", "filter", "group", "index" ] scripts = [ "basic", "database", "file", "filter", "group", "idle", "index" ]
for s in scripts: for s in scripts:
CONFIG.reset() CONFIG.reset()
CONFIG.TEST = True CONFIG.TEST = True

6
tests/idle/Sconscript Normal file
View File

@ -0,0 +1,6 @@
#!/usr/bin/python
Import("Test", "CONFIG")
CONFIG.IDLE = True
Test("idle", "idle.cpp")

64
tests/idle/idle.cpp Normal file
View File

@ -0,0 +1,64 @@
/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <idle.h>
#include <print.h>
#include <string>
struct TestStruct {
int i;
unsigned int u;
char c;
std::string s;
};
static void test_0(unsigned int &i)
{
print("Test 0: %u\n", i);
}
static void test_1(int &i)
{
print("Test 1: %d\n", i);
}
static void test_2(char &i)
{
print("Test 2: %c\n", i);
}
static void test_3(std::string &str)
{
print("Test 3: %s\n", str.c_str());
}
static void test_4(TestStruct &struc)
{
print("Test 4: %d %u %c %s\n", struc.i, struc.u, struc.c, struc.s.c_str());
}
static void test_5(TestStruct *&struc)
{
print("Test 5: %d %u %c %s\n", struc->i, struc->u, struc->c, struc->s.c_str());
}
int main(int argc, char **argv)
{
std::string string = "This is a string";
TestStruct struc1 = { -4, 4, 'd', "This is another string", };
TestStruct struc2 = { -5, 5, 'e', "This is yet another string", };
idle :: schedule(test_0, (unsigned int) 0);
idle :: schedule(test_1, 1);
idle :: schedule(test_2, 'b');
idle :: schedule(test_3, string);
idle :: schedule(test_4, struc1);
idle :: schedule(test_5, &struc2);
do {
print("Idle queue progress: %f\n", idle :: get_progress());
} while (idle :: run_task() == true);
print("Idle queue progress: %f\n", idle :: get_progress());
return 0;
}

13
tests/idle/idle.good Normal file
View File

@ -0,0 +1,13 @@
Idle queue progress: 0.000000
Test 0: 0
Idle queue progress: 0.166667
Test 1: 1
Idle queue progress: 0.333333
Test 2: b
Idle queue progress: 0.500000
Test 3: This is a string
Idle queue progress: 0.666667
Test 4: -4 4 d This is another string
Idle queue progress: 0.833333
Test 5: -5 5 e This is yet another string
Idle queue progress: 1.000000