Você está na página 1de 5

2/19/13

Function Pointers and Cooperative Multitasking

Beej's Bit Bucket


Like 0 Tw eet 0 0

T e c h a n d P r o g r a m m i n g F u n
1 2010-01-29

Function Pointers and Cooperative Multitasking


In the Good Old Days of Mac OS 9 and Windows 3.1, there existed a notion of multitasking known as cooperative multitasking. This is when the OS schedules tasks to run, and gives the tasks complete control of the CPU for as long as they want it. Tasks would voluntarily relinquish control after not too long, so that other tasks could run. Of course, a greedy task could just take over the system and not let anything else run, but such a task would lead to an unpleasant user experience, and so such software was unpopular. In other words, it was in the software's best interest to be cooperative.

Ama n a t e ev e r yq u i c k l yp e r f o r ms h i s" f l o a t " t a s kb e f o r ev o l u n t a r i l y r e t u r n i n gc o n t r o l t ot h ec o o p e r a t i v e mu l t i t a s k i n gs y s t e m.

Not could greedy tasks take over the system, but buggy ones could, as well! This would lead to system hangs, and user unhappiness. Real computer operating systems, like Unix, BSD, and Linux, shunned cooperative multitasking and instead used the superior idea of preemptive multitasking, in which the OS forcibly takes control away from a process when that process's time is up... no more hangs! All modern desktop OSes, including those from Microsoft and Apple, have left cooperative multitasking behind. So why even talk about it at all? Well, it's a good way to bring up another idea in C: function pointers. In an earlier article, I talked about pointers in C, which are variables that hold the memory address of other variables. Here's the fun bit: they can hold the memory address of functions, too! And then you can call functions indirectly via the pointer to the function! What's we're going is use this to set up a little cooperative multitasking system. But first let's get through some of the funky C syntax. We'll need to declare a variable that's of type "pointer to a function". We'll even be specific, and declare it to be a pointer to a function with a certain return type and a certain parameter list. E.g. "Declare a variable named 'functionpointer' that is a pointer to a function that returns an int and takes a c o n s tc h a r * as a parameter:
i n t( * f u n c t i o n p o i n t e r ) ( c o n s tc h a r* s ) ; / /t h i si saf u n c t i o np o i n t e r

At this point, we've declared a variable named "f u n c t i o n p o i n t e r ", but it doesn't actually point at anything. Now for comparison, here is a function prototype for a function that returns an i n t * :
i n t* f u n c t i o n p r o t o t y p e ( c o n s tc h a r* s ) ; / /f u n c t i o np r o t o t y p e ! !
beej.us/blog/data/function-pointers-cooperative-multitasking/ 1/5

2/19/13

Function Pointers and Cooperative Multitasking

i n t( * f u n c t i o n p o i n t e r ) ( c o n s tc h a r* s ) ; / /f u n c t i o np o i n t e r

Note the subtle difference: the parentheses. This lets the compiler know that the * is associated with the f u n c t i o n p o i n t e r , and not the i n t before it. So let's blow out this example into something big, and actually have the f u n c t i o n p o i n t e r point to a function, and then let's call it. The reason I made it return i n t and take the c o n s tc h a r * parameter was so that it would match another standard function in s t d i o . h , namely p u t s ( ) this function prints a string on the console. We'll get the address of p u t s ( ) , store it in f u n c t i o n p o i n t e r , and then call it. To get the address of a function, you refer to it by name without parentheses. That is, p u t s ( " H e l l o ! " ) calls the function normally, but p u t s is a pointer to the function. (You can preface it with ampersand if you want ("& p u t s "), but that's not idiomatic.) So we can store a pointer to p u t s ( ) in f u n c t i o n p o i n t e r just with the simple assignment:
f u n c t i o n p o i n t e r=p u t s ; / /f u n c t i o n p o i n t e rn o wp o i n t st op u t s

Now how do we call the function pointed to by the pointer? That is, how do we call p u t s ( ) via f u n c t i o n p o i n t e r ? Easy! We just add parenthesis and arguments and call it like a normal function:
f u n c t i o n p o i n t e r ( " H e l l o ,w o r l d ! " ) ;/ /j u s tl i k ep u t s ( " H e l l o ,w o r l d ! " )

And here's a full-blown example of the above stuff:


C

# i n c l u d e< s t d i o . h > i n tm a i n ( v o i d ) { / /d e c l a r eav a r i a b l en a m e d" f u n c t i o n p o i n t e r "t h a ti sap o i n t e rt oa / /f u n c t i o nt h a tr e t u r n sa n di n ta n dt a k e so n ep a r a m e t e r :ac o n s t / /c h a r* : i n t( * f u n c t i o n p o i n t e r ) ( c o n s tc h a r* s ) ; / /i n i t i a l i z ef u n c t i o n p o i n t e rt op o i n tt ot h eb u i l t i np u t s ( ) / /f u n c t i o n : f u n c t i o n p o i n t e r =p u t s ; / /n o wc a l lt h ef u n c t i o nv i at h ep o i n t e r : f u n c t i o n p o i n t e r ( " T h i si sj u s tl i k ec a l l i n gp u t s ( ) ! " ) ; } r e t u r n0 ;

Of course, all this so far have been for illustrative purposes and is marginally useful at best. Something in C that's actually common is to use function pointers in the library functions q s o r t ( ) and b s e a r c h ( ) . These functions accept as a parameter a pointer to a comparison function, which compares two values and returns the result. The q s o r t ( ) function uses the results of the comparison function to help sort an array. The advantage of this approach is that q s o r t ( ) 's behavior can
beej.us/blog/data/function-pointers-cooperative-multitasking/ 2/5

2/19/13

Function Pointers and Cooperative Multitasking

be modified by passing it pointers to different comparison functions, and a generalized q s o r t ( ) routine that can sort arbitrary types of data can be shipped for everyone to use. For instance, here is a program that sorts arrays. One array is sorted forward, and one is sorted backward. The only difference in the q s o r t ( ) call is the comparison function used:
C

# i n c l u d e< s t d i o . h > # i n c l u d e< s t d l i b . h > i n ts o r t _ a s c e n d i n g ( c o n s tv o i d* a ,c o n s tv o i d* b ) { r e t u r n* ( i n t * ) a> * ( i n t * ) b ; } i n ts o r t _ d e s c e n d i n g ( c o n s tv o i d* a ,c o n s tv o i d* b ) { r e t u r n* ( i n t * ) a< * ( i n t * ) b ; } i n tm a i n ( v o i d ) { i n ta r r a y 1 [ 5 ]={9 ,2 ,6 ,1 ,7} ; i n ta r r a y 2 [ 5 ]={9 ,2 ,6 ,1 ,7} ; / /g i v eq s o r t ( )ap o i n t e rt ot h e" s o r t _ a s c e n d i n g " / /c o m p a r i s o nf u n c t i o n : q s o r t ( a r r a y 1 ,5 ,s i z e o f ( i n t ) ,s o r t _ a s c e n d i n g ) ; / /g i v eq s o r t ( )ap o i n t e rt ot h e" s o r t _ d e s c e n d i n g " / /c o m p a r i s o nf u n c t i o n : q s o r t ( a r r a y 2 ,5 ,s i z e o f ( i n t ) ,s o r t _ d e s c e n d i n g ) ; } r e t u r n0 ;

Anyway, where were we? Oh yeahlet's see if we can use this to set up a simple cooperative multitasking system. The basic idea here is that we're going to have a bunch of "tasks", and each task will have an associated function that does all the work of that task. Each function will be called in turn, and each function is expected to return before too much time has elapsed (so it doesn't hog the system). We'll have a structure called t a s k l i s t that knows how many active tasks there are, and knows which functions are associated with those tasks. How many tasks is easy: it's an i n t . But we have to have a list of functions for each task... a list of pointers to functions... an array of pointers functions... an array of pointers to functions that return v o i d and have no parameters. Man, how do you declare that? Again the syntax is funky, but it is what it is. Here's an example declaration of an array of pointers to functions, with 10 elements:
v o i d( * t a s k _ f u n c t i o n [ 1 0 ] ) ( v o i d ) ;

There we've declared an array of pointers to functions, and that array is named "t a s k _ f u n c t i o n ". Here's an assignment to the third array element (index 2) and a
beej.us/blog/data/function-pointers-cooperative-multitasking/ 3/5

2/19/13

Function Pointers and Cooperative Multitasking

subsequent call to it:


t a s k _ f u n c t i o n [ 2 ]=f o o ; / /a s s i g nt op o i n tt of u n c t i o nf o o t a s k _ f u n c t i o n [ 2 ] ( ) ; / /c a l li t( c a l lf o o ( ) ,t h a ti s )

For this final example, we'll make up three tasks: one for Manatees, one for Goats, and one for Bats. Each task's function does a short thing and then returns control so it can go on to the next function. In m a i n ( ) the task list is initialized with three tasks, and the task function pointers are stored in the task function pointer array. The main loop then runs through the task list and dispatches each one in order, and then starts over again, round-robin style. In this example, it does it forever, but that's just my arbitrary decision.
C

# i n c l u d e< s t d i o . h > # d e f i n eM A X _ T A S K S1 0 s t r u c tt a s k l i s t{ i n tt a s k _ c o u n t ; v o i d( * t a s k _ f u n c t i o n [ M A X _ T A S K S ] ) ( v o i d ) ; } ; v o i dm a n a t e e _ f l o a t _ a n d _ g r a z e ( v o i d ) { p r i n t f ( " T h em a n a t e ei sf l o a t i n ga n dg r a z i n g . \ n " ) ; } v o i dg o a t _ s t a n d _ o n _ i t e m ( v o i d ) { p r i n t f ( " T h eg o a ti ss t a n d i n go na ni t e m . \ n " ) ; } v o i db a t _ e a t _ i n s e c t s ( v o i d ) { p r i n t f ( " T h eb a ti se a t i n gi n s e c t s . \ n " ) ; } i n tm a i n ( v o i d ) { i n ti ; s t r u c tt a s k l i s tt l ; t l . t a s k _ c o u n t=3 ; t l . t a s k _ f u n c t i o n [ 0 ]=m a n a t e e _ f l o a t _ a n d _ g r a z e ; t l . t a s k _ f u n c t i o n [ 1 ]=g o a t _ s t a n d _ o n _ i t e m ; t l . t a s k _ f u n c t i o n [ 2 ]=b a t _ e a t _ i n s e c t s ; w h i l e( 1 ){ p r i n t f( " D i s p a t c h i n g : \ n " ) ; f o r( i=0 ;i<t l . t a s k _ c o u n t ;i + + ){ t l . t a s k _ f u n c t i o n [ i ] ( ) ; / /e x e c u t et h et a s k ' sf u n c t i o n } } } r e t u r n0 ;

And this gives the following output:


D i s p a t c h i n g : T h em a n a t e ei sf l o a t i n ga n dg r a z i n g . T h eg o a ti ss t a n d i n go na ni t e m .
beej.us/blog/data/function-pointers-cooperative-multitasking/ 4/5

2/19/13

Function Pointers and Cooperative Multitasking

T h eb a ti se a t i n gi n s e c t s . D i s p a t c h i n g : T h em a n a t e ei sf l o a t i n ga n dg r a z i n g . T h eg o a ti ss t a n d i n go na ni t e m . T h eb a ti se a t i n gi n s e c t s . D i s p a t c h i n g : T h em a n a t e ei sf l o a t i n ga n dg r a z i n g . . . .[ f o r e v e r ]

Ways to improve this might be to pass a pointer to a data chunk to each task so it could maintain some state between calls. Or you could choose a different scheduling algorithm that would call tasks in an order other than round-robin. Or you could allow the system to dynamically create and destroy tasks as needed. For fun sometime in the future I might write a piece on scheduling and priorities and so on.

Share me!
Like 0 Tw eet 0 0 1

Historic Comments
Show

Comments 0 comments
Leave a message...

Com m unity Share

Discussion

No one has commented yet.

C o m m e n t fe e d

Su b s cri b e vi a e m a i l

Blog

Email beej@beej.us

Home page

beej.us/blog/data/function-pointers-cooperative-multitasking/

5/5

Você também pode gostar