summaryrefslogtreecommitdiffstats
path: root/pk/kernel/pk_semaphore_core.c
blob: 0e1e34d4d1330899069da57105ed71e938a36279 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file pk_semaphore_core.c
/// \brief PK semaphore APIs
///
///  The entry points in this file are considered 'core' routines that will
///  always be present at runtime in any PK application that enables
///  semaphores.

#include "pk.h"

/// Post a count to a semaphore
///
/// \param semaphore A pointer to the semaphore
///
/// If any thread is pending on the semaphore, the highest priority thread
/// will be made runnable and the internal count will remain 0.
///
/// If no thread is pending on the semaphore then the internal count will be
/// incremented by 1, with overflow wrapping the internal count through 0. If
/// the \a max_count argument supplied when the semaphore was created is
/// non-zero and the new internal count is greater than the \a max_count, an
/// overflow error will be signalled.
///
/// Return values other than PK_OK (0) are errors; see \ref pk_errors
///
/// \retval 0 Successful completion
///
/// \retval -PK_INVALID_SEMAPHORE_AT_POST The \a semaphore is a null (0) pointer.
/// 
/// \retval -PK_SEMAPHORE_OVERFLOW The \a max_count argument supplied when
/// the semaphore was created is non-zero and the new internal count is
/// greater than the \a max_count.

int
pk_semaphore_post(PkSemaphore *semaphore)
{
    PkMachineContext ctx;
    PkThreadPriority priority;

    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_POST);
    }

    pk_critical_section_enter(&ctx);

    priority = __pk_thread_queue_min(&(semaphore->pending_threads));

    if (priority != PK_IDLE_THREAD_PRIORITY) {

        __pk_thread_queue_delete(&(semaphore->pending_threads), priority);
        __pk_thread_queue_insert(&__pk_run_queue, priority);

        PK_KERN_TRACE("THREAD_SEMAPHORE_POST(%d)", priority);

        __pk_schedule();

    } else {

        semaphore->count++;

        if (PK_ERROR_CHECK_API) {
            PK_ERROR_IF((semaphore->max_count > 0) && 
                         (semaphore->count > semaphore->max_count),
                         PK_SEMAPHORE_OVERFLOW);
        }
    }

    pk_critical_section_exit(&ctx);
    
    return PK_OK;
}


/// Pend on a semaphore with timeout
///
/// \param semaphore A pointer to the semaphore
///
/// \param timeout A relative timeout in PK timebase ticks, including the
/// special values PK_NO_WAIT and PK_WAIT_FOREVER
///
/// This API is normally called from threads, and can only be successfully
/// called from interupt handlers under special conditions.
///
/// If the internal count of the \a semaphore is non-zero, the internal count
/// is decremented by one and execution of the caller continues.
///
/// If the internal count of the \a semaphore is zero and the \a timeout is
/// PK_NO_WAIT (0) then the call returns immediately with the informational
/// code -PK_SEMAPHORE_PEND_NO_WAIT.
///
/// If the internal count of the \a semaphore is zero and the \a timeout is
/// non-zero then a thread will block until either a semaphore count is
/// acquired or the relative timeout expires.  If this condition occurs in a
/// call from an interrupt context or before threads have been started then
/// the call will fail with the error \c -PK_SEMAPHORE_PEND_WOULD_BLOCK.
///
/// Once timed out the thread is removed from the semaphore pending queue and
/// made runnable, and the pk_semaphore_pend() operation will fail, even if
/// the semaphore count becomes available before the thread runs again. The
/// pk_semaphore_pend() API returns the informational code
/// -PK_SEMAPHORE_PEND_TIMED_OUT in this case.
///
/// By convention, a timeout interval equal to the maximum possible value of
/// the \c PkInterval type is taken to mean "wait forever".  A thread blocked
/// on a semaphore in this mode will never time out. PK provides this
/// constant as \c PK_WAIT_FOREVER.
///
/// Return values other than PK_OK (0) are not necessarily errors; see \ref
/// pk_errors 
///
/// The following return codes are non-error codes:
///
/// \retval 0 Successful completion
///
/// \retval -PK_SEMAPHORE_PEND_NO_WAIT timeout is set to PK_NO_WAIT
///
/// \retval -PK_SEMAPHORE_PEND_TIMED_OUT The semaphore was not acquired
/// before the timeout expired.
///
/// The following return codes are error codes:
///
/// \retval -PK_INVALID_SEMAPHORE_AT_PEND The \a semaphore is a null (0) 
/// pointer.
/// 
/// \retval -PK_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an
/// interrupt context (or before threads have been started), the semaphore
/// internal count was 0 and a non-zero timeout was specified.

// Note: Casting __pk_current_thread removes the 'volatile' attribute.

int
pk_semaphore_pend(PkSemaphore *semaphore,
                   PkInterval  timeout)
{
    PkMachineContext ctx;
    PkThreadPriority priority;
    PkThread         *thread;
    PkTimer          *timer = 0;
    PkInterval       scaled_timeout = PK_INTERVAL_SCALE(timeout);

    int rc = PK_OK;

    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_PEND);
    }

    pk_critical_section_enter(&ctx);

    if (semaphore->count != 0) {

        semaphore->count--;

    } else if (timeout == PK_NO_WAIT) {

        rc = -PK_SEMAPHORE_PEND_NO_WAIT;

    } else { 

        if (PK_ERROR_CHECK_API) {
            PK_ERROR_IF_CRITICAL(!__pk_kernel_context_thread(), 
                                  PK_SEMAPHORE_PEND_WOULD_BLOCK,
                                  &ctx);
        }

        thread = (PkThread *)__pk_current_thread;
        priority = thread->priority;

        __pk_thread_queue_insert(&(semaphore->pending_threads), priority);

        thread->semaphore = semaphore;
        thread->flags |= PK_THREAD_FLAG_SEMAPHORE_PEND;

        PK_KERN_TRACE("THREAD_SEMAPHORE_PEND(%d)", priority);

        if (timeout != PK_WAIT_FOREVER) {
            timer = &(thread->timer);
            timer->timeout = pk_timebase_get() + scaled_timeout;
            __pk_timer_schedule(timer);
            thread->flags |= PK_THREAD_FLAG_TIMER_PEND;
        }

        __pk_thread_queue_delete(&__pk_run_queue, priority);
        __pk_schedule();

        thread->flags &= ~PK_THREAD_FLAG_SEMAPHORE_PEND;

        if (thread->flags & PK_THREAD_FLAG_TIMER_PEND) {
            if (thread->flags & PK_THREAD_FLAG_TIMED_OUT) {
                rc = -PK_SEMAPHORE_PEND_TIMED_OUT;
                __pk_thread_queue_delete(&(semaphore->pending_threads), thread->priority);
            } else {
                __pk_timer_cancel(timer);
            }
            thread->flags &= 
                ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT);
        }
    }

    pk_critical_section_exit(&ctx);

    return rc;
}


/// Release all threads blocked on a semaphore
///
/// \param semaphore A pointer to a semaphore
///
/// This API is provided to allow an PK semaphore to be used as a thread
/// barrier. pk_semaphore_release_all() simultaneously unblocks all threads
/// (if any) currently pending on a semaphore. A semaphore to be used as a
/// thread barrier will typically be initialized with
/// pk_semaphore_create(\a sem, 0, 0), and sxx_semaphore_post() would never be
/// called on the \a sem.
///
/// This API never modifies the \a count field of the semaphore; If any
/// threads are blocked on a semaphore the semaphore count is 0 by definition.
///
/// Return values other than PK_OK (0) are errors; see \ref pk_errors
///
/// \retval 0 Successful completion
///
/// \retval -PK_INVALID_SEMAPHORE_AT_RELEASE The \a semaphore is a null (0) 
/// pointer.

int
pk_semaphore_release_all(PkSemaphore* semaphore)
{
    PkMachineContext ctx;

    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_RELEASE);
    }

    pk_critical_section_enter(&ctx);

    __pk_thread_queue_union(&__pk_run_queue, &(semaphore->pending_threads));
    __pk_thread_queue_clear(&(semaphore->pending_threads));
    __pk_schedule();

    pk_critical_section_exit(&ctx);

    return PK_OK;
}
                             
            
/// Get information about a semaphore.
///
/// \param semaphore A pointer to the PkSemaphore to query
///
/// \param count The value returned through this pointer is the current count
/// of the semaphore.  The caller can set this parameter to the null pointer
/// (0) if this information is not required.
///
/// \param pending The value returned through this pointer is the current
/// number of threads pending on the semaphore. The caller can set this
/// parameter to the null pointer (0) if this information is not required.
///
/// The information returned by this API can only be guaranteed consistent if
/// the API is called from a critical section.
///
/// Return values other than PK_OK (0) are errors; see \ref pk_errors
///
/// \retval 0 Successful completion
///
/// \retval -PK_INVALID_SEMAPHORE_AT_INFO The \a semaphore is a null (0) 
/// pointer.

int
pk_semaphore_info_get(PkSemaphore*      semaphore,
                       PkSemaphoreCount* count,
                       int*               pending)
                   
{
    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF(semaphore == 0, PK_INVALID_SEMAPHORE_AT_INFO);
    }

    if (count) {
        *count = semaphore->count;
    }
    if (pending) {
        *pending = __pk_thread_queue_count(&(semaphore->pending_threads));
    }

    return PK_OK;
}


/// An simple interrupt handler that posts to a semaphore.
///
/// To implement basic event-driven blocking of a thread, install
/// pk_semaphore_post_handler() as the handler for an interrupt
/// and provide a pointer to the semaphore as the \a arg argument in
/// pk_irq_handler_set().  The semaphore should be initialized with
/// pk_semaphore_create(&sem, 0, 1).  This handler simply disables (masks)
/// the interrupt, clears the status and calls pk_semaphore_post() on the
/// semaphore.  
///
/// Note that clearing the status in the interrupt controller as done here is
/// effectively a no-op for level-sensitive interrupts. In the level-sensitive
/// case any thread pending on the semaphore must reset the interrupt
/// condition in the device before re-enabling the interrupt.
#if 0
void
pk_semaphore_post_handler(void *arg, PkIrqId irq, int priority)
{
    pk_irq_disable(irq);
    pk_irq_status_clear(irq);
    pk_semaphore_post((PkSemaphore *)arg);
}

#endif
OpenPOWER on IntegriCloud