Not all tasks in an Ada program make use of the full functionality define d in the language reference manual . Certain idiomatic uses of Ada tasks allo w these tasks to be implemented as coroutines . This paper presents a metho d for mixing coroutines and processes to achieve a full implementation for Ad a tasking . This approach allows a variety of trade-offs to be made betwee n the level of concurrency, synchronization expense, and scheduling fairness . This, in turn, allows tuning the runtime support for specific applicatio n requirements and hardware capabilities .The semantics of Ada tasking as defined in the language reference manual is complex, taking into accoun t many possible uses of tasks and all possible interactions with other language features [Ada] . As a result, if an Ada system supplies only a single runtime implementation then it is typically complex relative to runtim e implementations for other languages . More importantly for users, it is typically expensive, both in term s of space and time . This expense can be reduced if the compiler recognizes frequently occurring idiomati c uses of tasking for which special purpose, less expensive implementations are possible . This paper discusse s efficient implementations to support tasking, and the circumstances in which they can be used .Habermann and Nassi proposed a dynamic selection scheme where the task to execute a rendezvous i s the one that reached it last, whether caller or callee [Habe80[ . This results in fewer context switches than a n implementation that makes this decision at compile time . They observed that some tasks containing negligible executable code outside of accept statements could be implemented in a way that strongly resemble d traditional monitors .Hilfinger developed the concept of monitor clusters, which are groups of tasks implemented as coroutine s [Hi1f82] . His implementation allows coroutines and "actual" Ada tasks to coexist in the same program b y having each "actual" task maintain a stack of ready coroutines . He then shows how this same implementatio n scheme can be modified slightly to support agent tasks, which primarily serve as message bearers .This paper builds on this work in a number of ways . A generalized notion of virtual task is presente d that combines Hilfinger's monitor clusters and agent tasks . Variations on this basic implementation provid e trade-offs between fairness and data structure complexity, and between the degree of concurrency and th e complexity of synchronization . The implementation can be extended to support nested entry calls and accep t statements, task priorities, delays, aborts, termination alternatives, and interrupt-driven task entries .