summaryrefslogtreecommitdiffstats
path: root/pk/kernel/pk_thread_util.c
blob: bf2e21b7e61c5ae8c845d95ae2681e04ce6560a9 (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
//-----------------------------------------------------------------------------
// *! (C) Copyright International Business Machines Corp. 2014
// *! All Rights Reserved -- Property of IBM
// *! *** IBM Confidential ***
//-----------------------------------------------------------------------------

/// \file pk_thread_util.c
/// \brief PK thread utility APIs
///
///  The entry points in this file are considered extra routines that will
///  only be included in a PK application that enables threads and uses at
///  least one of these interfaces.

#include "pk.h"
#include "pk_thread.h"

/// Get information about a thread.
///
/// \param thread A pointer to the PkThread to query
///
/// \param state The value returned through this pointer is the current state
/// of the thread; See \ref pk_thread_states. The caller can set this
/// parameter to the null pointer (0) if this information is not required.
///
/// \param priority The value returned through this pointer is the current
/// priority of the thread.  The caller can set this parameter to the null
/// pointer (0) if this information is not required.
///
/// \param runnable The value returned through this pointer is 1 if the thread
/// is in state PK_THREAD_STATE_MAPPED and is currently in the run queue
/// (i.e., neither blocked on a semaphore nor sleeping), otherwise 0. 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_THREAD_AT_INFO The \a thread is a null (0) pointer.

int
pk_thread_info_get(PkThread         *thread,
                    PkThreadState    *state,
                    PkThreadPriority *priority,
                    int               *runnable)
{
    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_INFO);
    }

    if (state) {
        *state = thread->state;
    }
    if (priority) {
        *priority = thread->priority;
    }
    if (runnable) {
        *runnable = ((thread->state == PK_THREAD_STATE_MAPPED) &&
                     __pk_thread_queue_member(&__pk_run_queue,
                                               thread->priority));
    }
    return PK_OK;
}


/// Change the priority of a thread.
///
/// \param thread The thread whose priority will be changed
///
/// \param new_priority The new priority of the thread
///
/// \param old_priority The value returned through this pointer is the
/// old priority of the thread prior to the change. The caller can set
/// this parameter to the null pointer (0) if this information is not
/// required.
///
/// Thread priorities can be changed by the \c pk_thread_priority_change()
/// API. This call will fail if the thread pointer is invalid or if the thread
/// is mapped and the new priority is currently in use.  The call will succeed
/// even if the \a thread is suspended, completed or deleted.  The
/// application-level scheduling algorithm is completely responsible for the
/// correctness of the application in the event of suspended, completed or
/// deleted threads.
///
/// Return values other than PK_OK (0) are errors; see \ref pk_errors
///
/// \retval 0 Successful completion, including the redundant case of
/// attempting to change the priority of the thread to its current priority.
///
/// \retval -PK_INVALID_THREAD_AT_CHANGE The \a thread is null (0) or 
/// otherwise invalid.
///
/// \retval -PK_INVALID_ARGUMENT_THREAD_CHANGE The \a new_priority is invalid.
///
/// \retval -PK_PRIORITY_IN_USE_AT_CHANGE The \a thread is mapped and the \a
/// new_priority is currently in use by another thread.

int 
pk_thread_priority_change(PkThread         *thread,
                           PkThreadPriority new_priority,
                           PkThreadPriority *old_priority)
{
    PkMachineContext ctx;
    PkThreadPriority priority;

    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_CHANGE);
        PK_ERROR_IF(new_priority > PK_THREADS, 
        PK_INVALID_ARGUMENT_THREAD_CHANGE);
    }

    pk_critical_section_enter(&ctx);

    priority = thread->priority;

    if (priority != new_priority) {

        if (!__pk_thread_is_mapped(thread)) {

            thread->priority = new_priority;

        } else {

            if (PK_ERROR_CHECK_API) {
                PK_ERROR_IF_CRITICAL(__pk_priority_map[new_priority] != 0,
                                      PK_PRIORITY_IN_USE_AT_CHANGE,
                                      &ctx);
            }

            __pk_thread_unmap(thread);
            thread->priority = new_priority;
            __pk_thread_map(thread);
            __pk_schedule();
        }
    }

    if (old_priority) {
        *old_priority = priority;
    }

    pk_critical_section_exit(&ctx);

    return PK_OK;
}


/// Return a pointer to the thread (if any) mapped at a given priority.
///
/// \param priority The thread priority of interest
///
/// \param thread The value returned through this pointer is a pointer to the
/// thread currently mapped at the given priority level.  If no thread is
/// mapped, or if the \a priority is the priority of the idle thread, the
/// pointer returned will be null (0).
///
/// 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_ARGUMENT_THREAD_PRIORITY The \a priority is invalid 
/// or the \a thread parameter is null (0). 

int
pk_thread_at_priority(PkThreadPriority priority,
                       PkThread         **thread)
{
    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF((priority > PK_THREADS) || (thread == 0),
                     PK_INVALID_ARGUMENT_THREAD_PRIORITY);
    }

    *thread = __pk_thread_at_priority(priority);

    return PK_OK;
}


/// Swap thread priorities
///
/// \param thread_a A pointer to an initialized PkThread
///
/// \param thread_b A pointer to an initialized PkThread
///
/// This API swaps the priorities of \a thread_a and \a thread_b.  The API is
/// provided to support general and efficient application-directed scheduling
/// algorithms.  The requirements on the \a thread_a and \a thread_b arguments
/// are that they are valid pointers to initialized PkThread structures, that
/// the current thread priorities of both threads are legal, and that if a
/// thread is currently mapped, that the new thread priority is not otherwise
/// in use.
///
/// The API does not require either thread to be mapped, or even to be active.
/// It is legal for one or both of the swap partners to be suspended, deleted
/// or completed threads.  The application is completely responsible for the
/// correctness of scheduling algorithms that might operate on inactive or
/// suspended threads.
///
/// The API does not change the mapped status of a thread.  A thread will be
/// mapped after the call of pk_thread_priority_swap() if and only if it was
/// mapped prior to the call.  If the new priority of a mapped thread is
/// currently in use (by a thread other than the swap partner), then the
/// PK_PRIORITY_IN_USE_AT_SWAP error is signalled and the swap does not take 
/// place. This could only happen if the swap partner is not currently mapped.
///
/// It is legal for a thread to swap its own priority with another thread. The
/// degenerate case that \a thread_a and \a thread_b are equal is also legal -
/// but has no effect.
///
/// Return values other than PK_OK (0) are errors; see \ref pk_errors
///
/// \retval 0 Successful completion, including the redundant cases that do not
/// actually change priorities, or the cases that assign new priorities to
/// suspended, completed or deleted threads.
///
/// \retval -PK_INVALID_THREAD_AT_SWAP1 One or both of \a thread_a and 
/// \a thread_b is null (0) or otherwise invalid, 
///
/// \retval -PK_INVALID_THREAD_AT_SWAP2 the priorities of One or both of 
/// \a thread_a and \a thread_b are invalid.
///
/// \retval -PK_INVALID_ARGUMENT One or both of the priorities
/// of \a thread_a and \a thread_b is invalid.
///
/// \retval -PK_PRIORITY_IN_USE_AT_SWAP Returned if a thread is mapped and the
/// new thread priority is currently in use by another thread (other than the
/// swap partner).

int
pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b)
{
    PkMachineContext ctx;
    PkThreadPriority priority_a, priority_b;
    int mapped_a, mapped_b;

    if (PK_ERROR_CHECK_API) {
        PK_ERROR_IF((thread_a == 0) ||  (thread_b == 0), 
                       PK_INVALID_THREAD_AT_SWAP1);
    }

    pk_critical_section_enter(&ctx);

    if (thread_a != thread_b) {

        mapped_a = __pk_thread_is_mapped(thread_a);
        mapped_b = __pk_thread_is_mapped(thread_b);
        priority_a = thread_a->priority;
        priority_b = thread_b->priority;

        if (PK_ERROR_CHECK_API) {
            int priority_in_use;
            PK_ERROR_IF_CRITICAL((priority_a > PK_THREADS) ||
                                  (priority_b > PK_THREADS),
                                  PK_INVALID_THREAD_AT_SWAP2,
                                  &ctx);
            priority_in_use = 
                (mapped_a && !mapped_b &&
                 (__pk_thread_at_priority(priority_b) != 0)) ||
                (!mapped_a && mapped_b && 
                 (__pk_thread_at_priority(priority_a) != 0));
            PK_ERROR_IF_CRITICAL(priority_in_use, 
                                  PK_PRIORITY_IN_USE_AT_SWAP, &ctx); 
        }

        if (mapped_a) {
            __pk_thread_unmap(thread_a);
        }
        if (mapped_b) {
            __pk_thread_unmap(thread_b);
        }            
        thread_a->priority = priority_b;
        thread_b->priority = priority_a;
        if (mapped_a) {
            __pk_thread_map(thread_a);
        }
        if (mapped_b) {
            __pk_thread_map(thread_b);
        }
        __pk_schedule();
    }

    pk_critical_section_exit(&ctx);

    return PK_OK;
}

OpenPOWER on IntegriCloud