Make OCProcessEvent method.
[platform/upstream/iotivity.git] / resource / c_common / ocevent / src / others / ocevent.c
1 /* *****************************************************************
2  *
3  * Copyright 2017 Microsoft
4  *
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  ******************************************************************/
19
20 /**
21  * @file
22  * This file implements Event object for allowing threads to wait on.
23  */
24
25 #include "ocevent.h"
26 #include "oic_malloc.h"
27 #include "oic_time.h"
28 #include "octhread.h"
29 #include "logger.h"
30 #include "iotivity_debug.h"
31
32 #include <assert.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35
36 /**
37  * TAG
38  * Logging tag for module name
39  */
40 #define TAG "OIC_EVENT"
41
42 typedef struct oc_event_t
43 {
44     /* Mutex for protecting the members. */
45     oc_mutex mutex;
46     /* The conditional variable to wait on. */
47     oc_cond cond;
48     /* Whether the event is signaled. */
49     bool signaled;
50 } oc_event_t;
51
52 oc_event oc_event_new(void)
53 {
54     oc_event event = (oc_event)OICCalloc(1, sizeof(oc_event_t));
55     if (!event)
56     {
57         OIC_LOG(ERROR, TAG, "Failed to allocate oc_event");
58         return NULL;
59     }
60
61     event->mutex = oc_mutex_new();
62     event->cond = oc_cond_new();
63     event->signaled = false;
64
65     if (!event->mutex || !event->cond)
66     {
67         oc_event_free(event);
68         return NULL;
69     }
70
71     return event;
72 }
73
74 void oc_event_free(oc_event event)
75 {
76     if (event)
77     {
78         oc_mutex_free(event->mutex);
79         oc_cond_free(event->cond);
80         OICFree(event);
81     }
82 }
83
84 void oc_event_wait(oc_event event)
85 {
86     OC_VERIFY(OC_WAIT_SUCCESS == oc_event_wait_for(event, UINT32_MAX));
87 }
88
89 OCWaitResult_t oc_event_wait_for(oc_event event, uint32_t milliseconds)
90 {
91     bool timedOut = false;
92     oc_mutex_assert_owner(event->mutex, false);
93     oc_mutex_lock(event->mutex);
94
95     if (!event->signaled)
96     {
97         if (0 != milliseconds)
98         {
99             const uint64_t startTime = OICGetCurrentTime(TIME_IN_MS);
100             uint64_t remaining = milliseconds;
101             // This while loop is to filter spurious wakeups caused by conditional variable.
102             while (!event->signaled)
103             {
104                 oc_mutex_assert_owner(event->mutex, true);
105                 OCWaitResult_t waitResult = oc_cond_wait_for(event->cond, event->mutex,
106                                                              (remaining * US_PER_MS));
107                 if (OC_WAIT_TIMEDOUT == waitResult)
108                 {
109                     timedOut = true;
110                     break;
111                 }
112                 assert(OC_WAIT_SUCCESS == waitResult);
113
114                 // Not timed out, see if the event is in signaled state and reset it.
115                 if (event->signaled)
116                 {
117                     timedOut = false;
118                     break;
119                 }
120
121                 // Not timed out and not signaled => spurious wakeup, see if we ran out of time.
122                 const uint64_t elapsed = (OICGetCurrentTime(TIME_IN_MS) - startTime);
123                 if (elapsed >= (uint64_t)milliseconds)
124                 {
125                     timedOut = true;
126                     break;
127                 }
128
129                 // Encountered spurious wakeup and still has time to wait, recalculate the
130                 // remaining time and wait again.
131                 // Spurious wakeup: depending on the platform, waiting on a Condition Variable can
132                 // occasionally (though rarely) return from the wait state even when the condition
133                 // isn't set, so we always need to revalidate the state in a loop here.
134                 remaining = (uint64_t)milliseconds - elapsed;
135             }
136         }
137         else
138         {
139             // Zero timeout provided and the event has not been signaled.
140             timedOut = true;
141         }
142     }
143     oc_mutex_assert_owner(event->mutex, true);
144     event->signaled = false;
145     oc_mutex_unlock(event->mutex);
146     return timedOut ? OC_WAIT_TIMEDOUT : OC_WAIT_SUCCESS;
147 }
148
149 void oc_event_signal(oc_event event)
150 {
151     oc_mutex_assert_owner(event->mutex, false);
152     oc_mutex_lock(event->mutex);
153     if (!event->signaled)
154     {
155         event->signaled = true;
156         oc_cond_signal(event->cond);
157     }
158     oc_mutex_unlock(event->mutex);
159 }