Adding a number of utilities

LocHeap, LocThread, LocTimer, and moved MsgTask to
utils. Also added are LocRankable and LocRunnable
abstract classes.

Change-Id: I76975455d24f2c84a95dcc1b04f79fea562ea5ba
CRs-Fixed: 855222
This commit is contained in:
Kevin Tang 2015-06-16 09:57:42 -07:00 committed by Vamana Murthi
parent 72993211dd
commit 58c3d32868
12 changed files with 1644 additions and 314 deletions

View file

@ -23,7 +23,6 @@ LOCAL_SHARED_LIBRARIES := \
libdl
LOCAL_SRC_FILES += \
MsgTask.cpp \
LocApiBase.cpp \
LocAdapterBase.cpp \
ContextBase.cpp \
@ -39,7 +38,6 @@ LOCAL_C_INCLUDES:= \
LOCAL_COPY_HEADERS_TO:= libloc_core/
LOCAL_COPY_HEADERS:= \
MsgTask.h \
LocApiBase.h \
LocAdapterBase.h \
ContextBase.h \

View file

@ -18,13 +18,17 @@ LOCAL_SRC_FILES += \
msg_q.c \
linked_list.c \
loc_target.cpp \
loc_timer.c \
platform_lib_abstractions/elapsed_millis_since_boot.cpp \
LocHeap.cpp \
LocTimer.cpp \
LocThread.cpp \
MsgTask.cpp \
loc_misc_utils.cpp
LOCAL_CFLAGS += \
-fno-short-enums \
-D_ANDROID_
-D_ANDROID_ \
-std=c++11
ifeq ($(TARGET_BUILD_VARIANT),user)
LOCAL_CFLAGS += -DTARGET_BUILD_VARIANT_USER
@ -43,6 +47,10 @@ LOCAL_COPY_HEADERS:= \
log_util.h \
linked_list.h \
msg_q.h \
MsgTask.h \
LocHeap.h \
LocThread.h \
LocTimer.h \
loc_target.h \
loc_timer.h \
platform_lib_abstractions/platform_lib_includes.h \

354
utils/LocHeap.cpp Normal file
View file

@ -0,0 +1,354 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <LocHeap.h>
class LocHeapNode {
friend class LocHeap;
// size of of the subtree, excluding self, 1 if no subtree
int mSize;
LocHeapNode* mLeft;
LocHeapNode* mRight;
LocRankable* mData;
public:
inline LocHeapNode(LocRankable& data) :
mSize(1), mLeft(NULL), mRight(NULL), mData(&data) {}
~LocHeapNode();
// this only swaps the data of the two nodes, so no
// detach / re-attached is necessary
void swap(LocHeapNode& node);
LocRankable* detachData();
// push a node into the tree stucture, keeping sorted by rank
void push(LocHeapNode& node);
// pop the head node out of the tree stucture. keeping sorted by rank
static LocHeapNode* pop(LocHeapNode*& top);
// remove a specific node from the tree
// returns the pointer to the node removed, which would be either the
// same as input (if successfully removed); or NULL (if failed).
static LocHeapNode* remove(LocHeapNode*& top, LocRankable& data);
// convenience method to compare data ranking
inline bool outRanks(LocHeapNode& node) { return mData->outRanks(*node.mData); }
inline bool outRanks(LocRankable& data) { return mData->outRanks(data); }
// checks if mSize is correct, AND this node is the highest ranking
// of the entire subtree
bool checkNodes();
inline int getSize() { return mSize; }
};
inline
LocHeapNode::~LocHeapNode() {
if (mLeft) {
delete mLeft;
mLeft = NULL;
}
if (mRight) {
delete mRight;
mRight = NULL;
}
if (mData) {
mData = NULL;
}
}
inline
void LocHeapNode::swap(LocHeapNode& node) {
LocRankable* tmpData = node.mData;
node.mData = mData;
mData = tmpData;
}
inline
LocRankable* LocHeapNode::detachData() {
LocRankable* data = mData;
mData = NULL;
return data;
}
// push keeps the tree sorted by rank, it also tries to balance the
// tree by adding the new node to the smaller of the subtrees.
// The pointer to the tree and internal links never change. If the
// mData of tree top ranks lower than that of the incoming node,
// mData will be swapped with that of the incoming node to ensure
// ranking, no restructuring the container nodes.
void LocHeapNode::push(LocHeapNode& node) {
// ensure the current node ranks higher than in the incoming one
if (node.outRanks(*this)) {
swap(node);
}
// now drop the new node (ensured lower than *this) into a subtree
if (NULL == mLeft) {
mLeft = &node;
} else if (NULL == mRight) {
mRight = &node;
} else if (mLeft->mSize <= mRight->mSize) {
mLeft->push(node);
} else {
mRight->push(node);
}
mSize++;
}
// pop keeps the tree sorted by rank, but it does not try to balance
// the tree. It recursively swaps with the higher ranked top of the
// subtrees.
// The return is a popped out node from leaf level, that has the data
// swapped all the way down from the top. The pinter to the tree and
// internal links will not be changed or restructured, except for the
// node that is popped out.
// If the return pointer == this, this the last node in the tree.
LocHeapNode* LocHeapNode::pop(LocHeapNode*& top) {
// we know the top has the highest ranking at this point, else
// the tree is broken. This top will be popped out. But we need
// a node from the left or right child, whichever ranks higher,
// to replace the current top. This then will need to be done
// recursively to the leaf level. So we swap the mData of the
// current top node all the way down to the leaf level.
LocHeapNode* poppedNode = top;
// top is losing a node in its subtree
top->mSize--;
if (top->mLeft || top->mRight) {
// if mLeft is NULL, mRight for sure is NOT NULL, take that;
// else if mRight is NULL, mLeft for sure is NOT, take that;
// else we take the address of whatever has higher ranking mData
LocHeapNode*& subTop = (NULL == top->mLeft) ? top->mRight :
((NULL == top->mRight) ? top->mLeft :
(top->mLeft->outRanks(*(top->mRight)) ? top->mLeft : top->mRight));
// swap mData, the tree top gets updated with the new data.
top->swap(*subTop);
// pop out from the subtree
poppedNode = pop(subTop);
} else {
// if the top has only single node
// detach the poppedNode from the tree
// subTop is the reference of ether mLeft or mRight
// NOT a local stack pointer. so it MUST be NULL'ed here.
top = NULL;
}
return poppedNode;
}
// navigating through the tree and find the node that hass the input
// data. Since this is a heap, we do recursive linear search.
// returns the pointer to the node removed, which would be either the
// same as input (if successfully removed); or NULL (if failed).
LocHeapNode* LocHeapNode::remove(LocHeapNode*& top, LocRankable& data) {
LocHeapNode* removedNode = NULL;
// this is the node, by address
if (&data == (LocRankable*)(top->mData)) {
// pop this node out
removedNode = pop(top);
} else if (!data.outRanks(*top->mData)) {
// subtrees might have this node
if (top->mLeft) {
removedNode = remove(top->mLeft, data);
}
// if we did not find in mLeft, and mRight is not empty
if (!removedNode && top->mRight) {
removedNode = remove(top->mRight, data);
}
// top lost a node in its subtree
if (removedNode) {
top->mSize--;
}
}
return removedNode;
}
// checks if mSize is correct, AND this node is the highest ranking
// of the entire subtree
bool LocHeapNode::checkNodes() {
// size of the current subtree
int totalSize = mSize;
if (mLeft) {
// check the consistency of left subtree
if (!outRanks(*mLeft) || !mLeft->checkNodes()) {
return false;
}
// subtract the size of left subtree (with subtree head)
totalSize -= mLeft->mSize;
}
if (mRight) {
// check the consistency of right subtree
if (!outRanks(*mRight) || !mRight->checkNodes()) {
return false;
}
// subtract the size of right subtree (with subtree head)
totalSize -= mRight->mSize;
}
// for the tree nodes to consistent, totalSize must be 1 now
return totalSize == 1;
}
LocHeap::~LocHeap() {
if (mTree) {
delete mTree;
}
}
void LocHeap::push(LocRankable& node) {
LocHeapNode* heapNode = new LocHeapNode(node);
if (!mTree) {
mTree = heapNode;
} else {
mTree->push(*heapNode);
}
}
LocRankable* LocHeap::peek() {
LocRankable* top = NULL;
if (mTree) {
top = mTree->mData;
}
return top;
}
LocRankable* LocHeap::pop() {
LocRankable* locNode = NULL;
if (mTree) {
// mTree may become NULL after this call
LocHeapNode* heapNode = LocHeapNode::pop(mTree);
locNode = heapNode->detachData();
delete heapNode;
}
return locNode;
}
LocRankable* LocHeap::remove(LocRankable& rankable) {
LocRankable* locNode = NULL;
if (mTree) {
// mTree may become NULL after this call
LocHeapNode* heapNode = LocHeapNode::remove(mTree, rankable);
if (heapNode) {
locNode = heapNode->detachData();
delete heapNode;
}
}
return locNode;
}
#ifdef __LOC_UNIT_TEST__
bool LocHeap::checkTree() {
return ((NULL == mTree) || mTree->checkNodes());
}
uint32_t LocHeap::getTreeSize() {
return (NULL == mTree) ? 0 : mTree->getSize();
}
#endif
#ifdef __LOC_DEBUG__
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
class LocHeapDebug : public LocHeap {
public:
bool checkTree() {
return ((NULL == mTree) || mTree->checkNodes());
}
uint32_t getTreeSize() {
return (NULL == mTree) ? 0 : (mTree->getSize());
}
};
class LocHeapDebugData : public LocRankable {
const int mID;
public:
LocHeapDebugData(int id) : mID(id) {}
inline virtual int ranks(LocRankable& rankable) {
LocHeapDebugData* testData = dynamic_cast<LocHeapDebugData*>(&rankable);
return testData->mID - mID;
}
};
// For Linux command line testing:
// compilation: g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../vendor/qcom/proprietary/gps-internal/unit-tests/fakes_for_host -I../../../../system/core/include LocHeap.cpp
// test: valgrind --leak-check=full ./a.out 100
int main(int argc, char** argv) {
srand(time(NULL));
int tries = atoi(argv[1]);
int checks = tries >> 3;
LocHeapDebug heap;
int treeSize = 0;
for (int i = 0; i < tries; i++) {
if (i % checks == 0 && !heap.checkTree()) {
printf("tree check failed before %dth op\n", i);
}
int r = rand();
if (r & 1) {
LocHeapDebugData* data = new LocHeapDebugData(r >> 1);
heap.push(dynamic_cast<LocRankable&>(*data));
treeSize++;
} else {
LocRankable* rankable = heap.pop();
if (rankable) {
delete rankable;
}
treeSize ? treeSize-- : 0;
}
printf("%s: %d == %d\n", (r&1)?"push":"pop", treeSize, heap.getTreeSize());
if (treeSize != heap.getTreeSize()) {
printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
tries = i+1;
break;
}
}
if (!heap.checkTree()) {
printf("!!!!!!!!!!tree check failed at the end after %d ops!!!!!!!\n", tries);
} else {
printf("success!\n");
}
for (LocRankable* data = heap.pop(); NULL != data; data = heap.pop()) {
delete data;
}
return 0;
}
#endif

96
utils/LocHeap.h Normal file
View file

@ -0,0 +1,96 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __LOC_HEAP__
#define __LOC_HEAP__
#include <stddef.h>
#include <string.h>
// abstract class to be implemented by client to provide a rankable class
class LocRankable {
public:
virtual inline ~LocRankable() {}
// method to rank objects of such type for sorting purposes.
// The pointer of the input node would be stored in the heap.
// >0 if ranks higher than the input;
// ==0 if equally ranks with the input;
// <0 if ranks lower than the input
virtual int ranks(LocRankable& rankable) = 0;
// convenient method to rank objects of such type for sorting purposes.
inline bool outRanks(LocRankable& rankable) { return ranks(rankable) > 0; }
};
// opaque class to provide service implementation.
class LocHeapNode;
// a heap whose left and right children are not sorted. It is sorted only vertically,
// i.e. parent always ranks higher than children, if they exist. Ranking algorithm is
// implemented in Rankable. The reason that there is no sort between children is to
// help beter balance the tree with lower cost. When a node is pushed to the tree,
// it is guaranteed that the subtree that is smaller gets to have the new node.
class LocHeap {
protected:
LocHeapNode* mTree;
public:
inline LocHeap() : mTree(NULL) {}
~LocHeap();
// push keeps the tree sorted by rank, it also tries to balance the
// tree by adding the new node to the smaller of the subtrees.
// node is reference to an obj that is managed by client, that client
// creates and destroyes. The destroy should happen after the
// node is popped out from the heap.
void push(LocRankable& node);
// Peeks the node data on tree top, which has currently the highest ranking
// There is no change the tree structure with this operation
// Returns NULL if the tree is empty, otherwise pointer to the node data of
// the tree top.
LocRankable* peek();
// pop keeps the tree sorted by rank, but it does not try to balance
// the tree.
// Return - pointer to the node popped out, or NULL if heap is already empty
LocRankable* pop();
// navigating through the tree and find the node that ranks the same
// as the input data, then remove it from the tree. Rank is implemented
// by rankable obj.
// returns the pointer to the node removed; or NULL (if failed).
LocRankable* remove(LocRankable& rankable);
#ifdef __LOC_UNIT_TEST__
bool checkTree();
uint32_t getTreeSize();
#endif
};
#endif //__LOC_HEAP__

252
utils/LocThread.cpp Normal file
View file

@ -0,0 +1,252 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <LocThread.h>
#include <string.h>
#include <pthread.h>
class LocThreadDelegate {
LocRunnable* mRunnable;
bool mJoinable;
pthread_t mThandle;
pthread_mutex_t mMutex;
int mRefCount;
~LocThreadDelegate();
LocThreadDelegate(const char* threadName,
LocRunnable* runnable, bool joinable);
void destroy();
public:
static LocThreadDelegate* create(const char* threadName,
LocRunnable* runnable, bool joinable);
void stop();
// bye() is for the parent thread to go away. if joinable,
// parent must stop the spawned thread, join, and then
// destroy(); if detached, the parent can go straight
// ahead to destroy()
inline void bye() { mJoinable ? stop() : destroy(); }
inline bool isRunning() { return (NULL != mRunnable); }
static void* threadMain(void* arg);
};
// it is important to note that internal members must be
// initialized to values as if pthread_create succeeds.
// This is to avoid the race condition between the threads,
// once the thread is created, some of these values will
// be check in the spawned thread, and must set correctly
// then and there.
// However, upon pthread_create failure, the data members
// must be set to indicate failure, e.g. mRunnable, and
// threashold approprietly for destroy(), e.g. mRefCount.
LocThreadDelegate::LocThreadDelegate(const char* threadName,
LocRunnable* runnable, bool joinable) :
mRunnable(runnable), mJoinable(joinable),
mMutex(PTHREAD_MUTEX_INITIALIZER), mRefCount(2) {
// create the thread here, then if successful
// and a name is given, we set the thread name
if (!pthread_create(&mThandle, NULL, threadMain, this)) {
// set thread name
char lname[16];
const char* defaultName = "LocThread";
if (!threadName) {
threadName = defaultName;
}
int len = sizeof(lname) - 1;
memcpy(lname, threadName, len);
lname[len] = 0;
// set the thread name here
pthread_setname_np(mThandle, lname);
// detach, if not joinable
if (!joinable) {
pthread_detach(mThandle);
}
} else {
// must set these values upon failure
mRunnable = NULL;
mJoinable = false;
mRefCount = 1;
}
}
inline
LocThreadDelegate::~LocThreadDelegate() {
// at this point nothing should need done any more
}
// factory method so that we could return NULL upon failure
LocThreadDelegate* LocThreadDelegate::create(const char* threadName,
LocRunnable* runnable, bool joinable) {
LocThreadDelegate* thread = NULL;
if (runnable) {
thread = new LocThreadDelegate(threadName, runnable, joinable);
if (thread && !thread->isRunning()) {
thread->destroy();
thread = NULL;
}
}
return thread;
}
// The order is importang
// NULLing mRunnalbe stops the while loop in threadMain()
// join() if mJoinble must come before destroy() call, as
// the obj must remain alive at this time so that mThandle
// remains valud.
void LocThreadDelegate::stop() {
// mRunnable and mJoinable are reset on different triggers.
// mRunnable may get nulled on the spawned thread's way out;
// or here.
// mJouinable (if ever been true) gets falsed when client
// thread triggers stop, with either a stop()
// call or the client releases thread obj handle.
if (mRunnable) {
mRunnable = NULL;
}
if (mJoinable) {
mJoinable = false;
pthread_join(mThandle, NULL);
}
// call destroy() to possibly delete the obj
destroy();
}
// method for clients to call to release the obj
// when it is a detached thread, the client thread
// and the spawned thread can both try to destroy()
// asynchronously. And we delete this obj when
// mRefCount becomes 0.
void LocThreadDelegate::destroy() {
// else case shouldn't happen, unless there is a
// leaking obj. But only our code here has such
// obj, so if we test our code well, else case
// will never happen
if (mRefCount > 0) {
// we need a flag on the stack
bool callDelete = false;
// critical section between threads
pthread_mutex_lock(&mMutex);
// last destroy() call
callDelete = (1 == mRefCount--);
pthread_mutex_unlock(&mMutex);
// upon last destroy() call we delete this obj
if (callDelete) {
delete this;
}
}
}
void* LocThreadDelegate::threadMain(void* arg) {
LocThreadDelegate* locThread = (LocThreadDelegate*)(arg);
if (locThread) {
LocRunnable* runnable = locThread->mRunnable;
if (runnable) {
if (locThread->isRunning()) {
runnable->prerun();
}
while (locThread->isRunning() && runnable->run());
if (locThread->isRunning()) {
runnable->postrun();
}
// at this time, locThread->mRunnable may or may not be NULL
// NULL it just to be safe and clean, as we want the field
// in the released memory slot to be NULL.
locThread->mRunnable = NULL;
delete runnable;
}
locThread->destroy();
}
return NULL;
}
LocThread::~LocThread() {
if (mThread) {
mThread->bye();
mThread = NULL;
}
}
bool LocThread::start(const char* threadName, LocRunnable* runnable, bool joinable) {
mThread = LocThreadDelegate::create(threadName, runnable, joinable);
// true only if thread is created successfully
return (NULL != mThread);
}
void LocThread::stop() {
if (mThread) {
mThread->stop();
mThread = NULL;
}
}
#ifdef __LOC_DEBUG__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
class LocRunnableTest1 : public LocRunnable {
int mID;
public:
LocRunnableTest1(int id) : LocRunnable(), mID(id) {}
virtual bool run() {
printf("LocRunnableTest1: %d\n", mID++);
sleep(1);
return true;
}
};
// on linux command line:
// compile: g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../vendor/qcom/proprietary/gps-internal/unit-tests/fakes_for_host -I../../../../system/core/include -lpthread LocThread.cpp
// test detached thread: valgrind ./a.out 0
// test joinable thread: valgrind ./a.out 1
int main(int argc, char** argv) {
LocRunnableTest1 test(10);
LocThread thread;
thread.start("LocThreadTest", test, atoi(argv[1]));
sleep(10);
thread.stop();
sleep(5);
return 0;
}
#endif

87
utils/LocThread.h Normal file
View file

@ -0,0 +1,87 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __LOC_THREAD__
#define __LOC_THREAD__
#include <stddef.h>
// abstract class to be implemented by client to provide a runnable class
// which gets scheduled by LocThread
class LocRunnable {
public:
inline LocRunnable() {}
inline virtual ~LocRunnable() {}
// The method to be implemented by thread clients
// and be scheduled by LocThread
// This method will be repeated called until it returns false; or
// until thread is stopped.
virtual bool run() = 0;
// The method to be run before thread loop (conditionally repeatedly)
// calls run()
inline virtual void prerun() {}
// The method to be run after thread loop (conditionally repeatedly)
// calls run()
inline virtual void postrun() {}
};
// opaque class to provide service implementation.
class LocThreadDelegate;
// A utility class to create a thread and run LocRunnable
// caller passes in.
class LocThread {
LocThreadDelegate* mThread;
public:
inline LocThread() : mThread(NULL) {}
virtual ~LocThread();
// client starts thread with a runnable, which implements
// the logics to fun in the created thread context.
// The thread could be either joinable or detached.
// runnable is an obj managed by client. Client creates and
// frees it (but must be after stop() is called, or
// this LocThread obj is deleted).
// The obj will be deleted by LocThread if start()
// returns true. Else it is client's responsibility
// to delete the object
// Returns 0 if success; false if failure.
bool start(const char* threadName, LocRunnable* runnable, bool joinable = true);
// NOTE: if this is a joinable thread, this stop may block
// for a while until the thread is joined.
void stop();
// thread status check
inline bool isRunning() { return NULL != mThread; }
};
#endif //__LOC_THREAD__

664
utils/LocTimer.cpp Normal file
View file

@ -0,0 +1,664 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <loc_timer.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <LocTimer.h>
#include <LocHeap.h>
#include <LocThread.h>
#include <pthread.h>
#include <MsgTask.h>
#ifdef __HOST_UNIT_TEST__
#define EPOLLWAKEUP 0
#define CLOCK_BOOTTIME CLOCK_MONOTONIC
#define CLOCK_BOOTTIME_ALARM CLOCK_MONOTONIC
#endif
using namespace loc_core;
/*
There are implementations of 5 classes in this file:
LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper
LocTimer - client front end, interface for client to start / stop timers, also
to provide a callback.
LocTimerDelegate - an internal timer entity, which also is a LocRankable obj.
Its life cycle is different than that of LocTimer. It gets
created when LocTimer::start() is called, and gets deleted
when it expires or clients calls the hosting LocTimer obj's
stop() method. When a LocTimerDelegate obj is ticking, it
stays in the corresponding LocTimerContainer. When expired
or stopped, the obj is removed from the container. Since it
is also a LocRankable obj, and LocTimerContainer also is a
heap, its ranks() implementation decides where it is placed
in the heap.
LocTimerContainer - core of the timer service. It is a container (derived from
LocHeap) for LocTimerDelegate (implements LocRankable) objs.
There are 2 of such containers, one for sw timers (or Linux
timers) one for hw timers (or Linux alarms). It adds one of
each (those that expire the soonest) to kernel via services
provided by LocTimerPollTask. All the heap management on the
LocTimerDelegate objs are done in the MsgTask context, such
that synchronization is ensured.
LocTimerPollTask - is a class that wraps timerfd and epoll POXIS APIs. It also
both implements LocRunnalbe with epoll_wait() in the run()
method. It is also a LocThread client, so as to loop the run
method.
LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
APIs, loc_timer_start() and loc_timer_stop().
*/
class LocTimerPollTask;
// This is a multi-functaional class that:
// * extends the LocHeap class for the detection of head update upon add / remove
// events. When that happens, soonest time out changes, so timerfd needs update.
// * contains the timers, and add / remove them into the heap
// * provides and maps 2 of such containers, one for timers (or mSwTimers), one
// for alarms (or mHwTimers);
// * provides a polling thread;
// * provides a MsgTask thread for synchronized add / remove / timer client callback.
class LocTimerContainer : public LocHeap {
// mutex to synchronize getters of static members
static pthread_mutex_t mMutex;
// Container of timers
static LocTimerContainer* mSwTimers;
// Container of alarms
static LocTimerContainer* mHwTimers;
// Msg task to provider msg Q, sender and reader.
static MsgTask* mMsgTask;
// Poll task to provide epoll call and threading to poll.
static LocTimerPollTask* mPollTask;
// timer / alarm fd
const int mDevFd;
// ctor
LocTimerContainer(bool wakeOnExpire);
// dtor
~LocTimerContainer();
static MsgTask* getMsgTaskLocked();
static LocTimerPollTask* getPollTaskLocked();
// extend LocHeap and pop if the top outRanks input
LocTimerDelegate* popIfOutRanks(LocTimerDelegate& timer);
public:
// factory method to control the creation of mSwTimers / mHwTimers
static LocTimerContainer* get(bool wakeOnExpire);
LocTimerDelegate* getSoonestTimer();
int getTimerFd();
// add a timer / alarm obj into the container
void add(LocTimerDelegate& timer);
// remove a timer / alarm obj from the container
void remove(LocTimerDelegate& timer);
// handling of timer / alarm expiration
void expire();
};
// This class implements the polling thread that epolls imer / alarm fds.
// The LocRunnable::run() contains the actual polling. The other methods
// will be run in the caller's thread context to add / remove timer / alarm
// fds the kernel, while the polling is blocked on epoll_wait() call.
// Since the design is that we have maximally 2 polls, one for all the
// timers; one for all the alarms, we will poll at most on 2 fds. But it
// is possile that all we have are only timers or alarms at one time, so we
// allow dynamically add / remove fds we poll on. The design decision of
// having 1 fd per container of timer / alarm is such that, we may not need
// to make a system call each time a timer / alarm is added / removed, unless
// that changes the "soonest" time out of that of all the timers / alarms.
class LocTimerPollTask : public LocRunnable {
// the epoll fd
const int mFd;
// the thread that calls run() method
LocThread* mThread;
friend class LocThreadDelegate;
// dtor
~LocTimerPollTask();
public:
// ctor
LocTimerPollTask();
// this obj will be deleted once thread is deleted
void destroy();
// add a container of timers. Each contain has a unique device fd, i.e.
// either timer or alarm fd, and a heap of timers / alarms. It is expected
// that container would have written to the device fd with the soonest
// time out value in the heap at the time of calling this method. So all
// this method does is to add the fd of the input container to the poll
// and also add the pointer of the container to the event data ptr, such
// when poll_wait wakes up on events, we know who is the owner of the fd.
void addPoll(LocTimerContainer& timerContainer);
// remove a fd that is assciated with a container. The expectation is that
// the atual timer would have been removed from the container.
void removePoll(LocTimerContainer& timerContainer);
// The polling thread context will call this method. This is where
// epoll_wait() is blocking and waiting for events..
virtual bool run();
};
// Internal class of timer obj. It gets born when client calls LocTimer::start();
// and gets deleted when client calls LocTimer::stop() or when the it expire()'s.
// This class implements LocRankable::ranks() so that when an obj is added into
// the container (of LocHeap), it gets placed in sorted order.
class LocTimerDelegate : public LocRankable {
friend class LocTimerContainer;
friend class LocTimer;
LocTimer* mClient;
struct timespec mFutureTime;
LocTimerContainer* mContainer;
// not a complete obj, just ctor for LocRankable comparisons
inline LocTimerDelegate(struct timespec& delay)
: mClient(NULL), mFutureTime(delay), mContainer(NULL) {}
inline ~LocTimerDelegate() {}
public:
LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire);
void destroy();
// LocRankable virtual method
virtual int ranks(LocRankable& rankable);
void expire();
inline struct timespec getFutureTime() { return mFutureTime; }
};
/***************************LocTimerContainer methods***************************/
// Most of these static recources are created on demand. They however are never
// destoyed. The theory is that there are processes that link to this util lib
// but never use timer, then these resources would never need to be created.
// For those processes that do use timer, it will likely also need to every
// once in a while. It might be cheaper keeping them around.
pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
MsgTask* LocTimerContainer::mMsgTask = NULL;
LocTimerPollTask* LocTimerContainer::mPollTask = NULL;
// ctor - initialize timer heaps
// A container for swTimer (timer) is created, when wakeOnExpire is true; or
// HwTimer (alarm), when wakeOnExpire is false.
LocTimerContainer::LocTimerContainer(bool wakeOnExpire) :
mDevFd(timerfd_create(wakeOnExpire ? CLOCK_BOOTTIME_ALARM : CLOCK_BOOTTIME, 0)) {
if (-1 != mDevFd) {
// ensure we have the necessary resources created
LocTimerContainer::getPollTaskLocked();
LocTimerContainer::getMsgTaskLocked();
} else {
LOC_LOGE("%s: timerfd_create failure - %s", __FUNCTION__, strerror(errno));
}
}
// dtor
// we do not ever destroy the static resources.
inline
LocTimerContainer::~LocTimerContainer() {
close(mDevFd);
}
LocTimerContainer* LocTimerContainer::get(bool wakeOnExpire) {
// get the reference of either mHwTimer or mSwTimers per wakeOnExpire
LocTimerContainer*& container = wakeOnExpire ? mHwTimers : mSwTimers;
// it is cheap to check pointer first than locking mutext unconditionally
if (!container) {
pthread_mutex_lock(&mMutex);
// let's check one more time to be safe
if (!container) {
container = new LocTimerContainer(wakeOnExpire);
// timerfd_create failure
if (-1 == container->getTimerFd()) {
delete container;
container = NULL;
}
}
pthread_mutex_unlock(&mMutex);
}
return container;
}
MsgTask* LocTimerContainer::getMsgTaskLocked() {
// it is cheap to check pointer first than locking mutext unconditionally
if (!mMsgTask) {
mMsgTask = new MsgTask("LocTimerMsgTask", false);
}
return mMsgTask;
}
LocTimerPollTask* LocTimerContainer::getPollTaskLocked() {
// it is cheap to check pointer first than locking mutext unconditionally
if (!mPollTask) {
mPollTask = new LocTimerPollTask();
}
return mPollTask;
}
inline
LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
return (LocTimerDelegate*)(peek());
}
inline
int LocTimerContainer::getTimerFd() {
return mDevFd;
}
// all the heap management is done in the MsgTask context.
inline
void LocTimerContainer::add(LocTimerDelegate& timer) {
struct MsgTimerPush : public LocMsg {
LocTimerContainer* mTimerContainer;
LocHeapNode* mTree;
LocTimerPollTask* mPollTask;
LocTimerDelegate* mTimer;
inline MsgTimerPush(LocTimerContainer& container, LocTimerPollTask& pollTask, LocTimerDelegate& timer) :
LocMsg(), mTimerContainer(&container), mPollTask(&pollTask), mTimer(&timer) {}
inline virtual void proc() const {
LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
mTimerContainer->push((LocRankable&)(*mTimer));
// if the tree top changed (new top is the new node), we need to update
// timerfd with the new timerout value.
if (priorTop != mTimerContainer->getSoonestTimer()) {
// if tree was empty before, we need to let poll task poll on this
// do this first to avoid race condition, in case settime is called
// with too small an interval
if (!priorTop) {
mPollTask->addPoll(*mTimerContainer);
}
struct itimerspec delay = {0};
delay.it_value = mTimer->getFutureTime();
timerfd_settime(mTimerContainer->getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
}
}
};
mMsgTask->sendMsg(new MsgTimerPush(*this, *mPollTask, timer));
}
// all the heap management is done in the MsgTask context.
void LocTimerContainer::remove(LocTimerDelegate& timer) {
struct MsgTimerRemove : public LocMsg {
LocTimerContainer* mTimerContainer;
LocTimerPollTask* mPollTask;
LocTimerDelegate* mTimer;
inline MsgTimerRemove(LocTimerContainer& container, LocTimerPollTask& pollTask, LocTimerDelegate& timer) :
LocMsg(), mTimerContainer(&container), mPollTask(&pollTask), mTimer(&timer) {}
inline virtual void proc() const {
LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer);
delete mTimer;
LocTimerDelegate* curTop = mTimerContainer->getSoonestTimer();
// if the tree top changed (the removed the node was the tree top), we need
// to update the timerfd with the new timeout value from the new top.
if (priorTop != curTop) {
struct itimerspec delay = {0};
// if tree is empty now, we need to remove poll from poll task
if (!curTop) {
mPollTask->removePoll(*mTimerContainer);
// setting the values to disarm timer
delay.it_value.tv_sec = 0;
delay.it_value.tv_nsec = 0;
} else {
delay.it_value = curTop->getFutureTime();
}
// this will either update the timer with the new soonest timeout
// or disarm the timer, if the current tree top empty
timerfd_settime(mTimerContainer->getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
}
}
};
mMsgTask->sendMsg(new MsgTimerRemove(*this, *mPollTask, timer));
}
// all the heap management is done in the MsgTask context.
// Upon expire, we check and continuously pop the heap until
// the top node's timeout is in the future.
void LocTimerContainer::expire() {
struct MsgTimerExpire : public LocMsg {
LocTimerContainer* mTimerContainer;
inline MsgTimerExpire(LocTimerContainer& container) :
LocMsg(), mTimerContainer(&container) {}
inline virtual void proc() const {
struct timespec now;
// get time spec of now
clock_gettime(CLOCK_BOOTTIME, &now);
LocTimerDelegate timerOfNow(now);
// pop everything in the heap that outRanks now, i.e. has time older than now
// and then call expire() on that timer.
for (LocTimerDelegate* timer = mTimerContainer->popIfOutRanks(timerOfNow);
NULL != timer;
timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
// the timer delegate obj will be deleted before the return of this call
timer->expire();
}
}
};
mMsgTask->sendMsg(new MsgTimerExpire(*this));
}
LocTimerDelegate* LocTimerContainer::popIfOutRanks(LocTimerDelegate& timer) {
LocTimerDelegate* poppedNode = NULL;
if (mTree && peek()->outRanks((LocRankable&)(timer))) {
poppedNode = (LocTimerDelegate*)(pop());
}
return poppedNode;
}
/***************************LocTimerPollTask methods***************************/
inline
LocTimerPollTask::LocTimerPollTask()
: mFd(epoll_create(2)), mThread(new LocThread()) {
// before a next call returens, a thread will be created. The run() method
// could already be running in parallel. Also, since each of the objs
// creates a thread, the container will make sure that there will be only
// one of such obj for our timer implementation.
if (!mThread->start("LocTimerPollTask", this)) {
delete mThread;
mThread = NULL;
}
}
inline
LocTimerPollTask::~LocTimerPollTask() {
// when fs is closed, epoll_wait() should fail run() should return false
// and the spawned thread should exit.
close(mFd);
}
void LocTimerPollTask::destroy() {
if (mThread) {
LocThread* thread = mThread;
mThread = NULL;
delete thread;
} else {
delete this;
}
}
void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
struct epoll_event ev;
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN | EPOLLWAKEUP;
// it is important that we set this context pointer with the input
// timer container this is how we know which container should handle
// which expiration.
ev.data.ptr = &timerContainer;
epoll_ctl(mFd, EPOLL_CTL_ADD, timerContainer.getTimerFd(), &ev);
}
inline
void LocTimerPollTask::removePoll(LocTimerContainer& timerContainer) {
epoll_ctl(mFd, EPOLL_CTL_DEL, timerContainer.getTimerFd(), NULL);
}
// The polling thread context will call this method. If run() method needs to
// be repetitvely called, it must return true from the previous call.
bool LocTimerPollTask::run() {
struct epoll_event ev[2];
// we have max 2 descriptors to poll from
int fds = epoll_wait(mFd, ev, 2, -1);
// we pretty much want to continually poll until the fd is closed
bool rerun = (fds > 0) || (errno == EINTR);
if (fds > 0) {
// we may have 2 events
for (int i = 0; i < fds; i++) {
// each fd will has a context pointer associated with the right timer container
LocTimerContainer* container = (LocTimerContainer*)(ev[i].data.ptr);
if (container) {
container->expire();
}
}
}
// if rerun is true, we are requesting to be scheduled again
return rerun;
}
/***************************LocTimerDelegate methods***************************/
inline
LocTimerDelegate::LocTimerDelegate(LocTimer& client, struct timespec& futureTime, bool wakeOnExpire)
: mClient(&client), mFutureTime(futureTime), mContainer(LocTimerContainer::get(wakeOnExpire)) {
// adding the timer into the container
mContainer->add(*this);
}
inline
void LocTimerDelegate::destroy() {
if (mContainer) {
mContainer->remove(*this);
mContainer = NULL;
}
}
int LocTimerDelegate::ranks(LocRankable& rankable) {
int rank = -1;
LocTimerDelegate* timer = (LocTimerDelegate*)(&rankable);
if (timer) {
// larger time ranks lower!!!
// IOW, if input obj has bigger tv_sec, this obj outRanks higher
rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
}
return rank;
}
inline
void LocTimerDelegate::expire() {
// keeping a copy of client pointer to be safe
// when timeOutCallback() is called at the end of this
// method, this obj is already deleted.
LocTimer* client = mClient;
// this obj is already removed from mContainer.
// NULL it here so that dtor won't try to call remove again
mContainer = NULL;
// force a stop, which will force a delete of this obj
mClient->stop();
// calling client callback with a pointer save on the stack
client->timeOutCallback();
}
/***************************LocTimer methods***************************/
bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
bool success = false;
if (!mTimer) {
struct timespec futureTime;
clock_gettime(CLOCK_BOOTTIME, &futureTime);
futureTime.tv_sec += timeOutInMs / 1000;
futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000;
if (futureTime.tv_nsec >= 1000000000) {
futureTime.tv_sec += futureTime.tv_nsec / 1000000000;
futureTime.tv_nsec %= 1000000000;
}
mTimer = new LocTimerDelegate(*this, futureTime, wakeOnExpire);
// if mTimer is non 0, success should be 0; or vice versa
success = (NULL != mTimer);
}
return success;
}
bool LocTimer::stop() {
bool success = false;
if (mTimer) {
mTimer->destroy();
mTimer = NULL;
success = true;
}
return success;
}
/***************************LocTimerWrapper methods***************************/
//////////////////////////////////////////////////////////////////////////
// This section below wraps for the C style APIs
//////////////////////////////////////////////////////////////////////////
class LocTimerWrapper : public LocTimer {
loc_timer_callback mCb;
void* mCallerData;
public:
inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
mCb(cb), mCallerData(callerData) {}
inline virtual void timeOutCallback() { mCb(mCallerData, 0); }
};
void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
void *caller_data, bool wake_on_expire)
{
LocTimerWrapper* locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);
if (locTimerWrapper) {
locTimerWrapper->start(msec, wake_on_expire);
}
return locTimerWrapper;
}
void loc_timer_stop(void*& handle)
{
if (handle) {
LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
locTimerWrapper->stop();
delete locTimerWrapper;
handle = NULL;
}
}
//////////////////////////////////////////////////////////////////////////
// This section above wraps for the C style APIs
//////////////////////////////////////////////////////////////////////////
#ifdef __LOC_DEBUG__
double getDeltaSeconds(struct timespec from, struct timespec to) {
return (double)to.tv_sec + (double)to.tv_nsec / 1000000000
- from.tv_sec - (double)from.tv_nsec / 1000000000;
}
struct timespec getNow() {
struct timespec now;
clock_gettime(CLOCK_BOOTTIME, &now);
return now;
}
class LocTimerTest : public LocTimer, public LocRankable {
int mTimeOut;
const struct timespec mTimeOfBirth;
inline struct timespec getTimerWrapper(int timeout) {
struct timespec now;
clock_gettime(CLOCK_BOOTTIME, &now);
now.tv_sec += timeout;
return now;
}
public:
inline LocTimerTest(int timeout) : LocTimer(), LocRankable(),
mTimeOut(timeout), mTimeOfBirth(getTimerWrapper(0)) {}
inline virtual int ranks(LocRankable& rankable) {
LocTimerTest* timer = dynamic_cast<LocTimerTest*>(&rankable);
return timer->mTimeOut - mTimeOut;
}
inline virtual void timeOutCallback() {
printf("timeOutCallback() - ");
deviation();
}
double deviation() {
struct timespec now = getTimerWrapper(0);
double delta = getDeltaSeconds(mTimeOfBirth, now);
printf("%lf: %lf\n", delta, delta * 100 / mTimeOut);
return delta / mTimeOut;
}
};
// For Linux command line testing:
// compilation:
// g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocHeap.o LocHeap.cpp
// g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../system/core/include -lpthread -o LocThread.o LocThread.cpp
// g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -I. -I../../../../system/core/include -o LocTimer.o LocTimer.cpp
int main(int argc, char** argv) {
struct timespec timeOfStart=getNow();
srand(time(NULL));
int tries = atoi(argv[1]);
int checks = tries >> 3;
LocTimerTest** timerArray = new LocTimerTest*[tries];
memset(timerArray, NULL, tries);
for (int i = 0; i < tries; i++) {
int r = rand() % tries;
LocTimerTest* timer = new LocTimerTest(r);
if (timerArray[r]) {
if (!timer->stop()) {
printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
printf("ERRER: %dth timer, id %d, not running when it should be\n", i, r);
exit(0);
} else {
printf("stop() - %d\n", r);
delete timer;
timerArray[r] = NULL;
}
} else {
if (!timer->start(r, false)) {
printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
printf("ERRER: %dth timer, id %d, running when it should not be\n", i, r);
exit(0);
} else {
printf("stop() - %d\n", r);
timerArray[r] = timer;
}
}
}
for (int i = 0; i < tries; i++) {
if (timerArray[i]) {
if (!timerArray[i]->stop()) {
printf("%lf:\n", getDeltaSeconds(timeOfStart, getNow()));
printf("ERRER: %dth timer, not running when it should be\n", i);
exit(0);
} else {
printf("stop() - %d\n", i);
delete timerArray[i];
timerArray[i] = NULL;
}
}
}
delete[] timerArray;
return 0;
}
#endif

67
utils/LocTimer.h Normal file
View file

@ -0,0 +1,67 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __LOC_TIMER_CPP_H__
#define __LOC_TIMER_CPP_H__
#include <stddef.h>
#include <log_util.h>
// opaque class to provide service implementation.
class LocTimerDelegate;
// LocTimer client must extend this class and implementthe callback.
// start() / stop() methods are to arm / disarm timer.
class LocTimer
{
LocTimerDelegate* mTimer;
public:
inline LocTimer() : mTimer(NULL) {}
inline virtual ~LocTimer() { stop(); }
// timeOutInMs: timeout delay in ms
// wakeOnExpire: true if to wake up CPU (if sleeping) upon timer
// expiration and notify the client.
// false if to wait until next time CPU wakes up (if
// sleeping) and then notify the client.
// return: true on success;
// false on failure, e.g. timer is already running.
bool start(uint32_t timeOutInMs, bool wakeOnExpire);
// return: true on success;
// false on failure, e.g. timer is not running.
bool stop();
// LocTimer client Should implement this method.
// This method is used for timeout calling back to client. This method
// should be short enough (eg: send a message to your own thread).
virtual void timeOutCallback() = 0;
};
#endif //__LOC_DELAY_H__

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2013,2015 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -38,66 +38,31 @@
namespace loc_core {
#define MAX_TASK_COMM_LEN 15
static void LocMsgDestroy(void* msg) {
delete (LocMsg*)msg;
}
MsgTask::MsgTask(tCreate tCreator, const char* threadName) :
mQ(msg_q_init2()), mAssociator(NULL){
if (tCreator) {
tCreator(threadName, loopMain,
(void*)new MsgTask(mQ, mAssociator));
} else {
createPThread(threadName);
MsgTask::MsgTask(const char* threadName, bool joinable) :
mQ(msg_q_init2()), mThread(new LocThread()) {
if (!mThread->start(threadName, this, joinable)) {
delete mThread;
mThread = NULL;
}
}
MsgTask::MsgTask(tAssociate tAssociator, const char* threadName) :
mQ(msg_q_init2()), mAssociator(tAssociator){
createPThread(threadName);
}
inline
MsgTask::MsgTask(const void* q, tAssociate associator) :
mQ(q), mAssociator(associator){
}
MsgTask::~MsgTask() {
msg_q_flush((void*)mQ);
msg_q_destroy((void**)&mQ);
}
void MsgTask::destroy() {
msg_q_unblock((void*)mQ);
}
void MsgTask::associate(tAssociate tAssociator) const {
struct LocAssociateMsg : public LocMsg {
tAssociate mAssociator;
inline LocAssociateMsg(tAssociate associator) :
LocMsg(), mAssociator(associator) {}
inline virtual void proc() const {
if (mAssociator) {
LOC_LOGD("MsgTask::associate");
mAssociator();
}
}
};
sendMsg(new LocAssociateMsg(tAssociator));
}
void MsgTask::createPThread(const char* threadName) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t tid;
// create the thread here, then if successful
// and a name is given, we set the thread name
if (!pthread_create(&tid, &attr, loopMain,
(void*)new MsgTask(mQ, mAssociator)) &&
NULL != threadName) {
char lname[MAX_TASK_COMM_LEN+1];
memcpy(lname, threadName, MAX_TASK_COMM_LEN);
lname[MAX_TASK_COMM_LEN] = 0;
pthread_setname_np(tid, lname);
if (mThread) {
LocThread* thread = mThread;
mThread = NULL;
delete thread;
} else {
delete this;
}
}
@ -105,43 +70,63 @@ void MsgTask::sendMsg(const LocMsg* msg) const {
msg_q_snd((void*)mQ, (void*)msg, LocMsgDestroy);
}
void* MsgTask::loopMain(void* arg) {
MsgTask* copy = (MsgTask*)arg;
void MsgTask::prerun() {
// make sure we do not run in background scheduling group
set_sched_policy(gettid(), SP_FOREGROUND);
}
if (NULL != copy->mAssociator) {
copy->mAssociator();
}
bool MsgTask::run() {
LOC_LOGD("MsgTask::loop() listening ...\n");
LocMsg* msg;
int cnt = 0;
while (1) {
LOC_LOGD("MsgTask::loop() %d listening ...\n", cnt++);
msq_q_err_type result = msg_q_rcv((void*)copy->mQ, (void **)&msg);
if (eMSG_Q_SUCCESS != result) {
LOC_LOGE("%s:%d] fail receiving msg: %s\n", __func__, __LINE__,
loc_get_msg_q_status(result));
// destroy the Q and exit
msg_q_destroy((void**)&(copy->mQ));
delete copy;
return NULL;
}
msg->log();
// there is where each individual msg handling is invoked
msg->proc();
delete msg;
msq_q_err_type result = msg_q_rcv((void*)mQ, (void **)&msg);
if (eMSG_Q_SUCCESS != result) {
LOC_LOGE("%s:%d] fail receiving msg: %s\n", __func__, __LINE__,
loc_get_msg_q_status(result));
return false;
}
delete copy;
msg->log();
// there is where each individual msg handling is invoked
msg->proc();
return NULL;
delete msg;
return true;
}
// TODO: remove the below in the next patch
void MsgTask::associate(tAssociate tAssociator) const {
struct LocAssociateMsg : public LocMsg {
tAssociate mAssociator;
LocAssociateMsg(tAssociate associator) :
mAssociator(associator) {}
inline virtual void proc() const {
static bool sAssociated = false;
if (!sAssociated) {
sAssociated = true;
mAssociator();
}
}
};
sendMsg(new LocAssociateMsg(tAssociator));
}
MsgTask::MsgTask(tCreate tCreator, const char* threadName) :
mQ(msg_q_init2()), mThread(new LocThread()) {
if (!mThread->start(threadName, this, false)) {
delete mThread;
mThread = NULL;
}
}
MsgTask::MsgTask(tAssociate tAssociator, const char* threadName) :
mQ(msg_q_init2()), mThread(new LocThread()) {
if (!mThread->start(threadName, this, false)) {
delete mThread;
mThread = NULL;
} else {
associate(tAssociator);
}
}
} // namespace loc_core

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2013,2015 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -29,9 +29,8 @@
#ifndef __MSG_TASK__
#define __MSG_TASK__
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <LocThread.h>
// TODO: remove this include in the next patch
#include <pthread.h>
namespace loc_core {
@ -43,23 +42,37 @@ struct LocMsg {
inline virtual void log() const {}
};
class MsgTask {
class MsgTask : public LocRunnable{
const void* mQ;
LocThread* mThread;
friend class LocThreadDelegate;
protected:
virtual ~MsgTask();
public:
MsgTask(const char* threadName = NULL, bool joinable = true);
// this obj will be deleted once thread is deleted
void destroy();
void sendMsg(const LocMsg* msg) const;
// Overrides of LocRunnable methods
// This method will be repeated called until it returns false; or
// until thread is stopped.
virtual bool run();
// The method to be run before thread loop (conditionally repeatedly)
// calls run()
virtual void prerun();
// The method to be run after thread loop (conditionally repeatedly)
// calls run()
inline virtual void postrun() {}
// TODO: remove the below in the next patch
typedef void* (*tStart)(void*);
typedef pthread_t (*tCreate)(const char* name, tStart start, void* arg);
typedef int (*tAssociate)();
MsgTask(tCreate tCreator, const char* threadName);
MsgTask(tAssociate tAssociator, const char* threadName);
~MsgTask();
void sendMsg(const LocMsg* msg) const;
void associate(tAssociate tAssociator) const;
private:
const void* mQ;
tAssociate mAssociator;
MsgTask(const void* q, tAssociate associator);
static void* loopMain(void* copy);
void createPThread(const char* name);
};
} // namespace loc_core

View file

@ -1,202 +0,0 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/time.h>
#include "loc_timer.h"
#include<time.h>
#include<errno.h>
enum timer_state {
READY = 100,
WAITING,
DONE,
ABORT
};
typedef struct {
loc_timer_callback callback_func;
void *user_data;
unsigned int time_msec;
pthread_cond_t timer_cond;
pthread_mutex_t timer_mutex;
enum timer_state state;
}timer_data;
static void *timer_thread(void *thread_data)
{
int ret = -ETIMEDOUT;
struct timespec ts;
struct timeval tv;
timer_data* t = (timer_data*)thread_data;
LOC_LOGD("%s:%d]: Enter. Delay = %d\n", __func__, __LINE__, t->time_msec);
gettimeofday(&tv, NULL);
clock_gettime(CLOCK_REALTIME, &ts);
if(t->time_msec >= 1000) {
ts.tv_sec += t->time_msec/1000;
t->time_msec = t->time_msec % 1000;
}
if(t->time_msec)
ts.tv_nsec += t->time_msec * 1000000;
if(ts.tv_nsec > 999999999) {
LOC_LOGD("%s:%d]: Large nanosecs\n", __func__, __LINE__);
ts.tv_sec += 1;
ts.tv_nsec -= 1000000000;
}
LOC_LOGD("%s:%d]: ts.tv_sec:%d; ts.tv_nsec:%d\n"
"\t Current time: %d sec; %d nsec",
__func__, __LINE__, (int)ts.tv_sec, (int)ts.tv_nsec,
(int)tv.tv_sec, (int)tv.tv_usec*1000);
pthread_mutex_lock(&(t->timer_mutex));
if (READY == t->state) {
t->state = WAITING;
ret = pthread_cond_timedwait(&t->timer_cond, &t->timer_mutex, &ts);
t->state = DONE;
}
pthread_mutex_unlock(&(t->timer_mutex));
switch (ret) {
case ETIMEDOUT:
LOC_LOGV("%s:%d]: loc_timer timed out", __func__, __LINE__);
break;
case 0:
LOC_LOGV("%s:%d]: loc_timer stopped", __func__, __LINE__);
break;
case -ETIMEDOUT:
LOC_LOGV("%s:%d]: loc_timer cancelled", __func__, __LINE__);
break;
default:
LOC_LOGE("%s:%d]: Call to pthread timedwait failed; ret=%d\n",
__func__, __LINE__, ret);
break;
}
if(ETIMEDOUT == ret)
t->callback_func(t->user_data, ret);
// A (should be rare) race condition is that, when the loc_time_stop is called
// and acquired mutex, we reach here. pthread_mutex_destroy will fail with
// error code EBUSY. We give it 6 tries in 5 seconds. Should be eanough time
// for loc_timer_stop to complete. With the 7th try, we also perform unlock
// prior to destroy.
{
int i;
for (i = 0; EBUSY == pthread_mutex_destroy(&t->timer_mutex) && i <= 5; i++) {
if (i < 5) {
sleep(1);
} else {
// nah, forget it, something is seriously wrong. Mutex has been
// held too long. Unlock the mutext here.
pthread_mutex_unlock(&t->timer_mutex);
}
}
}
pthread_cond_destroy(&t->timer_cond);
free(t);
LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__);
return NULL;
}
void* loc_timer_start(unsigned int msec, loc_timer_callback cb_func,
void* caller_data)
{
timer_data *t=NULL;
pthread_attr_t tattr;
pthread_t id;
LOC_LOGD("%s:%d]: Enter\n", __func__, __LINE__);
if(cb_func == NULL || msec == 0) {
LOC_LOGE("%s:%d]: Error: Wrong parameters\n", __func__, __LINE__);
goto _err;
}
t = (timer_data *)calloc(1, sizeof(timer_data));
if(t == NULL) {
LOC_LOGE("%s:%d]: Could not allocate memory. Failing.\n",
__func__, __LINE__);
goto _err;
}
if(pthread_cond_init(&(t->timer_cond), NULL)) {
LOC_LOGE("%s:%d]: Pthread cond init failed\n", __func__, __LINE__);
goto t_err;
}
if(pthread_mutex_init(&(t->timer_mutex), NULL)) {
LOC_LOGE("%s:%d]: Pthread mutex init failed\n", __func__, __LINE__);
goto cond_err;
}
t->callback_func = cb_func;
t->user_data = caller_data;
t->time_msec = msec;
t->state = READY;
if (pthread_attr_init(&tattr)) {
LOC_LOGE("%s:%d]: Pthread mutex init failed\n", __func__, __LINE__);
goto mutex_err;
}
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
if(pthread_create(&(id), &tattr, timer_thread, (void *)t)) {
LOC_LOGE("%s:%d]: Could not create thread\n", __func__, __LINE__);
goto attr_err;
}
LOC_LOGD("%s:%d]: Created thread with id: %d\n",
__func__, __LINE__, (int)id);
goto _err;
attr_err:
pthread_attr_destroy(&tattr);
mutex_err:
pthread_mutex_destroy(&t->timer_mutex);
cond_err:
pthread_cond_destroy(&t->timer_cond);
t_err:
free(t);
_err:
LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__);
return t;
}
void loc_timer_stop(void* handle) {
timer_data* t = (timer_data*)handle;
if (NULL != t && (READY == t->state || WAITING == t->state) &&
pthread_mutex_lock(&(t->timer_mutex)) == 0) {
if (READY == t->state || WAITING == t->state) {
pthread_cond_signal(&t->timer_cond);
t->state = ABORT;
}
pthread_mutex_unlock(&(t->timer_mutex));
}
}

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013,2015 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -33,28 +33,36 @@
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include<pthread.h>
#include "log_util.h"
#include <stddef.h>
/*
Return values:
Success = 0
Failure = Non zero
user_data: client context pointer, passthrough. Originally received
from calling client when loc_timer_start() is called.
result: 0 if timer successfully timed out; else timer failed.
*/
typedef void(*loc_timer_callback)(void *user_data, int result);
typedef void (*loc_timer_callback)(void *user_data, int32_t result);
/*
Returns the handle, which can be used to stop the timer
delay_msec: timeout value for the timer.
loc_timer_callback: callback function pointer, implemented by client.
user_data: client context pointer, passthrough. Will be
returned when loc_timer_callback() is called.
wakeOnExpire: true if to wake up CPU (if sleeping) upon timer
expiration and notify the client.
false if to wait until next time CPU wakes up (if
sleeping) and then notify the client.
Returns the handle, which can be used to stop the timer
*/
void* loc_timer_start(unsigned int delay_msec,
void* loc_timer_start(uint64_t delay_msec,
loc_timer_callback,
void* user_data);
void *user_data,
bool wake_on_expire=false);
/*
handle becomes invalid upon the return of the callback
handle becomes invalid upon the return of the callback
*/
void loc_timer_stop(void* handle);
void loc_timer_stop(void*& handle);
#ifdef __cplusplus
}