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:
parent
72993211dd
commit
58c3d32868
12 changed files with 1644 additions and 314 deletions
|
@ -23,7 +23,6 @@ LOCAL_SHARED_LIBRARIES := \
|
||||||
libdl
|
libdl
|
||||||
|
|
||||||
LOCAL_SRC_FILES += \
|
LOCAL_SRC_FILES += \
|
||||||
MsgTask.cpp \
|
|
||||||
LocApiBase.cpp \
|
LocApiBase.cpp \
|
||||||
LocAdapterBase.cpp \
|
LocAdapterBase.cpp \
|
||||||
ContextBase.cpp \
|
ContextBase.cpp \
|
||||||
|
@ -39,7 +38,6 @@ LOCAL_C_INCLUDES:= \
|
||||||
|
|
||||||
LOCAL_COPY_HEADERS_TO:= libloc_core/
|
LOCAL_COPY_HEADERS_TO:= libloc_core/
|
||||||
LOCAL_COPY_HEADERS:= \
|
LOCAL_COPY_HEADERS:= \
|
||||||
MsgTask.h \
|
|
||||||
LocApiBase.h \
|
LocApiBase.h \
|
||||||
LocAdapterBase.h \
|
LocAdapterBase.h \
|
||||||
ContextBase.h \
|
ContextBase.h \
|
||||||
|
|
|
@ -18,13 +18,17 @@ LOCAL_SRC_FILES += \
|
||||||
msg_q.c \
|
msg_q.c \
|
||||||
linked_list.c \
|
linked_list.c \
|
||||||
loc_target.cpp \
|
loc_target.cpp \
|
||||||
loc_timer.c \
|
|
||||||
platform_lib_abstractions/elapsed_millis_since_boot.cpp \
|
platform_lib_abstractions/elapsed_millis_since_boot.cpp \
|
||||||
|
LocHeap.cpp \
|
||||||
|
LocTimer.cpp \
|
||||||
|
LocThread.cpp \
|
||||||
|
MsgTask.cpp \
|
||||||
loc_misc_utils.cpp
|
loc_misc_utils.cpp
|
||||||
|
|
||||||
LOCAL_CFLAGS += \
|
LOCAL_CFLAGS += \
|
||||||
-fno-short-enums \
|
-fno-short-enums \
|
||||||
-D_ANDROID_
|
-D_ANDROID_ \
|
||||||
|
-std=c++11
|
||||||
|
|
||||||
ifeq ($(TARGET_BUILD_VARIANT),user)
|
ifeq ($(TARGET_BUILD_VARIANT),user)
|
||||||
LOCAL_CFLAGS += -DTARGET_BUILD_VARIANT_USER
|
LOCAL_CFLAGS += -DTARGET_BUILD_VARIANT_USER
|
||||||
|
@ -43,6 +47,10 @@ LOCAL_COPY_HEADERS:= \
|
||||||
log_util.h \
|
log_util.h \
|
||||||
linked_list.h \
|
linked_list.h \
|
||||||
msg_q.h \
|
msg_q.h \
|
||||||
|
MsgTask.h \
|
||||||
|
LocHeap.h \
|
||||||
|
LocThread.h \
|
||||||
|
LocTimer.h \
|
||||||
loc_target.h \
|
loc_target.h \
|
||||||
loc_timer.h \
|
loc_timer.h \
|
||||||
platform_lib_abstractions/platform_lib_includes.h \
|
platform_lib_abstractions/platform_lib_includes.h \
|
||||||
|
|
354
utils/LocHeap.cpp
Normal file
354
utils/LocHeap.cpp
Normal 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
96
utils/LocHeap.h
Normal 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
252
utils/LocThread.cpp
Normal 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
87
utils/LocThread.h
Normal 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
664
utils/LocTimer.cpp
Normal 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
67
utils/LocTimer.h
Normal 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__
|
|
@ -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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are
|
* modification, are permitted provided that the following conditions are
|
||||||
|
@ -38,66 +38,31 @@
|
||||||
|
|
||||||
namespace loc_core {
|
namespace loc_core {
|
||||||
|
|
||||||
#define MAX_TASK_COMM_LEN 15
|
|
||||||
|
|
||||||
static void LocMsgDestroy(void* msg) {
|
static void LocMsgDestroy(void* msg) {
|
||||||
delete (LocMsg*)msg;
|
delete (LocMsg*)msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
MsgTask::MsgTask(tCreate tCreator, const char* threadName) :
|
MsgTask::MsgTask(const char* threadName, bool joinable) :
|
||||||
mQ(msg_q_init2()), mAssociator(NULL){
|
mQ(msg_q_init2()), mThread(new LocThread()) {
|
||||||
if (tCreator) {
|
if (!mThread->start(threadName, this, joinable)) {
|
||||||
tCreator(threadName, loopMain,
|
delete mThread;
|
||||||
(void*)new MsgTask(mQ, mAssociator));
|
mThread = NULL;
|
||||||
} else {
|
|
||||||
createPThread(threadName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
MsgTask::~MsgTask() {
|
||||||
|
msg_q_flush((void*)mQ);
|
||||||
|
msg_q_destroy((void**)&mQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MsgTask::destroy() {
|
||||||
msg_q_unblock((void*)mQ);
|
msg_q_unblock((void*)mQ);
|
||||||
}
|
if (mThread) {
|
||||||
|
LocThread* thread = mThread;
|
||||||
void MsgTask::associate(tAssociate tAssociator) const {
|
mThread = NULL;
|
||||||
struct LocAssociateMsg : public LocMsg {
|
delete thread;
|
||||||
tAssociate mAssociator;
|
} else {
|
||||||
inline LocAssociateMsg(tAssociate associator) :
|
delete this;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,31 +70,19 @@ void MsgTask::sendMsg(const LocMsg* msg) const {
|
||||||
msg_q_snd((void*)mQ, (void*)msg, LocMsgDestroy);
|
msg_q_snd((void*)mQ, (void*)msg, LocMsgDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* MsgTask::loopMain(void* arg) {
|
void MsgTask::prerun() {
|
||||||
MsgTask* copy = (MsgTask*)arg;
|
|
||||||
|
|
||||||
// make sure we do not run in background scheduling group
|
// make sure we do not run in background scheduling group
|
||||||
set_sched_policy(gettid(), SP_FOREGROUND);
|
set_sched_policy(gettid(), SP_FOREGROUND);
|
||||||
|
|
||||||
if (NULL != copy->mAssociator) {
|
|
||||||
copy->mAssociator();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MsgTask::run() {
|
||||||
|
LOC_LOGD("MsgTask::loop() listening ...\n");
|
||||||
LocMsg* msg;
|
LocMsg* msg;
|
||||||
int cnt = 0;
|
msq_q_err_type result = msg_q_rcv((void*)mQ, (void **)&msg);
|
||||||
|
|
||||||
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) {
|
if (eMSG_Q_SUCCESS != result) {
|
||||||
LOC_LOGE("%s:%d] fail receiving msg: %s\n", __func__, __LINE__,
|
LOC_LOGE("%s:%d] fail receiving msg: %s\n", __func__, __LINE__,
|
||||||
loc_get_msg_q_status(result));
|
loc_get_msg_q_status(result));
|
||||||
// destroy the Q and exit
|
return false;
|
||||||
msg_q_destroy((void**)&(copy->mQ));
|
|
||||||
delete copy;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg->log();
|
msg->log();
|
||||||
|
@ -137,11 +90,43 @@ void* MsgTask::loopMain(void* arg) {
|
||||||
msg->proc();
|
msg->proc();
|
||||||
|
|
||||||
delete msg;
|
delete msg;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete copy;
|
// TODO: remove the below in the next patch
|
||||||
|
void MsgTask::associate(tAssociate tAssociator) const {
|
||||||
return NULL;
|
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
|
|
@ -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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are
|
* modification, are permitted provided that the following conditions are
|
||||||
|
@ -29,9 +29,8 @@
|
||||||
#ifndef __MSG_TASK__
|
#ifndef __MSG_TASK__
|
||||||
#define __MSG_TASK__
|
#define __MSG_TASK__
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <LocThread.h>
|
||||||
#include <ctype.h>
|
// TODO: remove this include in the next patch
|
||||||
#include <string.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
namespace loc_core {
|
namespace loc_core {
|
||||||
|
@ -43,23 +42,37 @@ struct LocMsg {
|
||||||
inline virtual void log() const {}
|
inline virtual void log() const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MsgTask {
|
class MsgTask : public LocRunnable{
|
||||||
|
const void* mQ;
|
||||||
|
LocThread* mThread;
|
||||||
|
friend class LocThreadDelegate;
|
||||||
|
protected:
|
||||||
|
virtual ~MsgTask();
|
||||||
public:
|
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 void* (*tStart)(void*);
|
||||||
typedef pthread_t (*tCreate)(const char* name, tStart start, void* arg);
|
typedef pthread_t (*tCreate)(const char* name, tStart start, void* arg);
|
||||||
typedef int (*tAssociate)();
|
typedef int (*tAssociate)();
|
||||||
MsgTask(tCreate tCreator, const char* threadName);
|
MsgTask(tCreate tCreator, const char* threadName);
|
||||||
MsgTask(tAssociate tAssociator, const char* threadName);
|
MsgTask(tAssociate tAssociator, const char* threadName);
|
||||||
~MsgTask();
|
|
||||||
void sendMsg(const LocMsg* msg) const;
|
|
||||||
void associate(tAssociate tAssociator) 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
|
} // namespace loc_core
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are
|
* modification, are permitted provided that the following conditions are
|
||||||
|
@ -33,28 +33,36 @@
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
#include<pthread.h>
|
#include <stddef.h>
|
||||||
#include "log_util.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Return values:
|
user_data: client context pointer, passthrough. Originally received
|
||||||
Success = 0
|
from calling client when loc_timer_start() is called.
|
||||||
Failure = Non zero
|
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);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
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
|
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,
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue