summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/Android.mk2
-rw-r--r--utils/Android.mk12
-rw-r--r--utils/LocHeap.cpp354
-rw-r--r--utils/LocHeap.h96
-rw-r--r--utils/LocThread.cpp252
-rw-r--r--utils/LocThread.h87
-rw-r--r--utils/LocTimer.cpp664
-rw-r--r--utils/LocTimer.h67
-rw-r--r--utils/MsgTask.cpp (renamed from core/MsgTask.cpp)139
-rw-r--r--utils/MsgTask.h (renamed from core/MsgTask.h)41
-rw-r--r--utils/loc_timer.c202
-rw-r--r--utils/loc_timer.h32
12 files changed, 1639 insertions, 309 deletions
diff --git a/core/Android.mk b/core/Android.mk
index 6a08b6a..267b31f 100644
--- a/core/Android.mk
+++ b/core/Android.mk
@@ -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 \
diff --git a/utils/Android.mk b/utils/Android.mk
index d672e3a..c9b1788 100644
--- a/utils/Android.mk
+++ b/utils/Android.mk
@@ -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 \
diff --git a/utils/LocHeap.cpp b/utils/LocHeap.cpp
new file mode 100644
index 0000000..4126878
--- /dev/null
+++ b/utils/LocHeap.cpp
@@ -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
diff --git a/utils/LocHeap.h b/utils/LocHeap.h
new file mode 100644
index 0000000..b491948
--- /dev/null
+++ b/utils/LocHeap.h
@@ -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__
diff --git a/utils/LocThread.cpp b/utils/LocThread.cpp
new file mode 100644
index 0000000..ec1e071
--- /dev/null
+++ b/utils/LocThread.cpp
@@ -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
diff --git a/utils/LocThread.h b/utils/LocThread.h
new file mode 100644
index 0000000..490d309
--- /dev/null
+++ b/utils/LocThread.h
@@ -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__
diff --git a/utils/LocTimer.cpp b/utils/LocTimer.cpp
new file mode 100644
index 0000000..2209809
--- /dev/null
+++ b/utils/LocTimer.cpp
@@ -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
diff --git a/utils/LocTimer.h b/utils/LocTimer.h
new file mode 100644
index 0000000..c501292
--- /dev/null
+++ b/utils/LocTimer.h
@@ -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__
diff --git a/core/MsgTask.cpp b/utils/MsgTask.cpp
index 5d375aa..8b645dd 100644
--- a/core/MsgTask.cpp
+++ b/utils/MsgTask.cpp
@@ -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_unblock((void*)mQ);
+ msg_q_flush((void*)mQ);
+ msg_q_destroy((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);
+void MsgTask::destroy() {
+ msg_q_unblock((void*)mQ);
+ 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;
+ 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;
}
- LocMsg* msg;
- int cnt = 0;
+ msg->log();
+ // there is where each individual msg handling is invoked
+ msg->proc();
- while (1) {
- LOC_LOGD("MsgTask::loop() %d listening ...\n", cnt++);
+ delete msg;
- msq_q_err_type result = msg_q_rcv((void*)copy->mQ, (void **)&msg);
+ return true;
+}
- 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;
+// 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));
+}
- msg->log();
- // there is where each individual msg handling is invoked
- msg->proc();
-
- delete msg;
+MsgTask::MsgTask(tCreate tCreator, const char* threadName) :
+ mQ(msg_q_init2()), mThread(new LocThread()) {
+ if (!mThread->start(threadName, this, false)) {
+ delete mThread;
+ mThread = NULL;
}
-
- delete copy;
-
- return 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
diff --git a/core/MsgTask.h b/utils/MsgTask.h
index d50bb31..c397ee1 100644
--- a/core/MsgTask.h
+++ b/utils/MsgTask.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
* 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
diff --git a/utils/loc_timer.c b/utils/loc_timer.c
deleted file mode 100644
index 2beca5f..0000000
--- a/utils/loc_timer.c
+++ /dev/null
@@ -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));
- }
-}
diff --git a/utils/loc_timer.h b/utils/loc_timer.h
index 0034d27..8836d1e 100644
--- a/utils/loc_timer.h
+++ b/utils/loc_timer.h
@@ -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
}