on 3/25/05 7:00 PM, Rhoads Hollowell at email@hidden wrote:
> The solution is to start a new kernel thread to make the Syncronous
> calls. Look in the header file thread_call.h and in the IOUSBFamily
> sources in Darwin for examples on how to do this. We do it all the time
> in the IOUSBFamily.
That does indeed seem like the right approach to take, and I should have
thought of it myself. Thanks for pointing it out.
The main issue I've had with trying to use the thread_call.h functions up
until now is the total lack of any documentation on them, aside from some
uncommented code examples, and some minimal comments in the actual
thread_call source module. In particular, the area that concerned me a lot
is how threads get terminated at exit time.
After analyzing the source code, I made a quick stab for my own reference at
documenting what I believe these APIs do. By posting it here at the end of
this message, it might also be useful for others who need to use these APIs.
I'd appreciate it if you (or anyone else familiar with this code at Apple)
would correct this if there are mistakes.
Thanks,
-->Steve Bennett
thread_call API
===============
(Take all this with a grain of salt unless someone at Apple confirms it's
accuracy... :-)
Discussion:
This is an API to control threading usable from within the kernel and in
IOKit kexts. Most of the information I could find on the web seems to
recommend using these functions since they are less resource intensive than
the IOCreateThread() related functions described in the IOKit documentation.
Apple uses them extensively in some areas of the IOKit.
The thread_call API creates a shared, reusable, pool of threads to handle
multiple thread function calls, creating new threads only as needed (when
all the other threads are in use and blocked...) reusing them as long as
there is demand for them, and keeping a small number of them around even
when there isn't demand.
This API is fairly easy to use - you simply call thread_call_allocate() to
create a "thread call" which references your thread function, then call one
of four different thread_call_entry...() routines to schedule your thread
function for calling on a separate thread. You can do this repeatedly as
needed. When you are done doing all threaded stuff, you release the thread
call using thread_call_free().
Your thread function must be of the format:
void function(thread_call_param_t param0, thread_call_param_t param1);
...where param0 and param1 are both void pointers. The function should do
whatever it needs to do and return -- it doesn't need to do (and shouldn't
attempt) any thread termination code and doesn't need to make any thread
calls at all for that matter.
This API, while efficient, is also very raw -- It's best suited for
procedural routines which do a particular something and then return. (Doing
a series of synchronous I/O calls for example...) If you want threads which
loop for long periods and never return (such as event-driven loops), or
which contain long calculations that do not block, you would be better off
(IMHO) making your own thread with IOCreateThread in that case.
Also you're on your own to make sure your function doesn't accidentally run
multiple times (or can handle it if it does...), and that it's actually
finished running when you clean up. (See the notes below on the
thread_call_entry...() routines and the thread_call_free() routine for more
on this...)
Functions:
thread_call_t thread_call_allocate(thread_call_func_t func,
thread_call_param_t param0);
- Creates a "thread call" handle, where func is the function which will be
called back (as described above), and param0 is the first parameter to that
function. The returned call is passed to all the remaining functions. You
can keep this thread call around as long as you like, but you should release
it with thread_call_free() before you exit.
Most of the code I've found which uses this never checks for an error
return, but I assume it will return NULL if it can't allocate the
thread_call_t structure, and always recommend checking the results...
boolean_t thread_call_cancel(thread_call_t call);
- If a thread call has been scheduled using any of the
thread_call_enter...() functions below, but is still pending (ie. The thread
function has not yet been called at all), then this function will remove the
thread call from the pending schedule. It will return true if it had a
pending call to remove, false otherwise.
boolean_t thread_call_enter1_delayed(thread_call_t call,
thread_call_param_t param1, uint64_t deadline);
- Schedules the thread function in the given thread call a single time. The
thread function will be called with it's first parameter as given in the
thread_call_allocate(), and it's second parameter set to param1.
The call will happen when the current clock_get_uptime() value is greater
than the given deadline value, and one of the threads in the thread pool
becomes available for use. (Hmmn... Isn't clock_get_uptime() sometimes
running at the wrong speed on some machines because the processor speed
sometimes changes due to power management? I guess you shouldn't expect the
deadline time to necessarily be accurate...)
Returns false if the call had no pending schedule. Returns true if the the
call had already been scheduled but hadn't yet been actually called, in
which case this schedule replaces the previously scheduled call. (In other
words, if you called any of the other enter functions before this, and this
one returns true, then that previous call and it's parameter and/or deadline
is ignored and this call takes precedence. Effectively, each
thread_call_enter...() function behaves as if you called
thread_call_cancel() first and returned it's result...)
Note that it will return false if the thread function is *currently* running
as well -- in that case it will schedule the thread function again. (It's
not very clear to me -- does this mean we could theoretically end up with
two threads running that same thread function?!? I can't see anything which
would stop that from happening, especially if the thread function takes
quite a bit of time to finish through blocking I/O...)
boolean_t thread_call_enter_delayed(thread_call_t call, uint64_t deadline);
- Identical to thread_call_enter1_delayed() except that the second parameter
to the thread function will be set to zero.
boolean_t thread_call_enter(thread_call_t call);
- Identical to thread_call_enter1_delayed() except that the second parameter
to the thread function will be set to zero and the call will happen as soon
as a thread from the thread pool is available for use.
boolean_t thread_call_enter1(thread_call_t call,
thread_call_param_t param1);
- Identical to thread_call_enter1_delayed except that the call will happen
as soon as a thread from the thread pool becomes available for use.
boolean_t thread_call_is_delayed(thread_call_t call, uint64_t *deadline);
- If a thread call has been scheduled and has been given a deadline which is
still in the future, then this function will return true. If the given
deadline pointer is not NULL, it returns the scheduled deadline value.
boolean_t thread_call_free(thread_call_t call);
- This function will block until there is no pending schedule on the given
thread call, then will release the thread call. (If you call
thread_call_cancel() first, then thread_call_free() ought to return
immediately...)
Note that if the thread function is *currently* running, this function may
very well return before that thread function has finished executing. It's
up to you to handle any synchronization issues that might come into play
here - I recommend using the IOLock mechanism for that.
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Usb mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/usb/email@hidden
This email sent to email@hidden