… | |
… | |
505 | coro_func func; |
505 | coro_func func; |
506 | void *arg; |
506 | void *arg; |
507 | coro_context *self, *main; |
507 | coro_context *self, *main; |
508 | }; |
508 | }; |
509 | |
509 | |
510 | static pthread_t null_tid; |
|
|
511 | |
|
|
512 | /* I'd so love to cast pthread_mutex_unlock to void (*)(void *)... */ |
|
|
513 | static void |
|
|
514 | mutex_unlock_wrapper (void *arg) |
|
|
515 | { |
|
|
516 | pthread_mutex_unlock ((pthread_mutex_t *)arg); |
|
|
517 | } |
|
|
518 | |
|
|
519 | static void * |
510 | static void * |
520 | coro_init (void *args_) |
511 | coro_init (void *args_) |
521 | { |
512 | { |
522 | struct coro_init_args *args = (struct coro_init_args *)args_; |
513 | struct coro_init_args *args = (struct coro_init_args *)args_; |
523 | coro_func func = args->func; |
514 | coro_func func = args->func; |
524 | void *arg = args->arg; |
515 | void *arg = args->arg; |
525 | |
516 | |
526 | pthread_mutex_lock (&coro_mutex); |
|
|
527 | |
|
|
528 | /* we try to be good citizens and use deferred cancellation and cleanup handlers */ |
|
|
529 | pthread_cleanup_push (mutex_unlock_wrapper, &coro_mutex); |
|
|
530 | coro_transfer (args->self, args->main); |
517 | coro_transfer (args->self, args->main); |
531 | func (arg); |
518 | func (arg); |
532 | pthread_cleanup_pop (1); |
|
|
533 | |
519 | |
534 | return 0; |
520 | return 0; |
535 | } |
521 | } |
536 | |
522 | |
537 | void |
523 | void |
538 | coro_transfer (coro_context *prev, coro_context *next) |
524 | coro_transfer (coro_context *prev, coro_context *next) |
539 | { |
525 | { |
|
|
526 | pthread_mutex_lock (&coro_mutex); |
|
|
527 | |
|
|
528 | next->flags = 1; |
540 | pthread_cond_signal (&next->cv); |
529 | pthread_cond_signal (&next->cv); |
|
|
530 | |
|
|
531 | prev->flags = 0; |
|
|
532 | |
|
|
533 | while (!prev->flags) |
541 | pthread_cond_wait (&prev->cv, &coro_mutex); |
534 | pthread_cond_wait (&prev->cv, &coro_mutex); |
542 | #if __FreeBSD__ /* freebsd is of course broken and needs manual testcancel calls... yay... */ |
535 | |
543 | pthread_testcancel (); |
536 | if (prev->flags == 2) |
544 | #endif |
537 | { |
|
|
538 | pthread_mutex_unlock (&coro_mutex); |
|
|
539 | pthread_cond_destroy (&prev->cv); |
|
|
540 | pthread_detach (pthread_self ()); |
|
|
541 | pthread_exit (0); |
|
|
542 | } |
|
|
543 | |
|
|
544 | pthread_mutex_unlock (&coro_mutex); |
545 | } |
545 | } |
546 | |
546 | |
547 | void |
547 | void |
548 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, size_t ssize) |
548 | coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, size_t ssize) |
549 | { |
549 | { |
… | |
… | |
552 | |
552 | |
553 | if (!once) |
553 | if (!once) |
554 | { |
554 | { |
555 | once = 1; |
555 | once = 1; |
556 | |
556 | |
557 | pthread_mutex_lock (&coro_mutex); |
|
|
558 | pthread_cond_init (&nctx.cv, 0); |
557 | pthread_cond_init (&nctx.cv, 0); |
559 | null_tid = pthread_self (); |
|
|
560 | } |
558 | } |
561 | |
559 | |
562 | pthread_cond_init (&ctx->cv, 0); |
560 | pthread_cond_init (&ctx->cv, 0); |
563 | |
561 | |
564 | if (coro) |
562 | if (coro) |
565 | { |
563 | { |
566 | pthread_attr_t attr; |
564 | pthread_attr_t attr; |
567 | struct coro_init_args args; |
565 | struct coro_init_args args; |
|
|
566 | pthread_t id; |
568 | |
567 | |
569 | args.func = coro; |
568 | args.func = coro; |
570 | args.arg = arg; |
569 | args.arg = arg; |
571 | args.self = ctx; |
570 | args.self = ctx; |
572 | args.main = &nctx; |
571 | args.main = &nctx; |
… | |
… | |
580 | pthread_attr_setstacksize (&attr, (size_t)ssize); |
579 | pthread_attr_setstacksize (&attr, (size_t)ssize); |
581 | #else |
580 | #else |
582 | pthread_attr_setstack (&attr, sptr, (size_t)ssize); |
581 | pthread_attr_setstack (&attr, sptr, (size_t)ssize); |
583 | #endif |
582 | #endif |
584 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); |
583 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); |
585 | pthread_create (&ctx->id, &attr, coro_init, &args); |
584 | pthread_create (&id, &attr, coro_init, &args); |
586 | |
585 | |
587 | coro_transfer (args.main, args.self); |
586 | coro_transfer (args.main, args.self); |
588 | } |
587 | } |
589 | else |
|
|
590 | ctx->id = null_tid; |
|
|
591 | } |
588 | } |
592 | |
589 | |
593 | void |
590 | void |
594 | coro_destroy (coro_context *ctx) |
591 | coro_destroy (coro_context *ctx) |
595 | { |
592 | { |
596 | if (!pthread_equal (ctx->id, null_tid)) |
|
|
597 | { |
|
|
598 | pthread_cancel (ctx->id); |
|
|
599 | pthread_mutex_unlock (&coro_mutex); /* let the other coro run */ |
|
|
600 | pthread_join (ctx->id, 0); |
|
|
601 | pthread_mutex_lock (&coro_mutex); |
593 | pthread_mutex_lock (&coro_mutex); |
602 | } |
594 | ctx->flags = 2; |
603 | |
|
|
604 | pthread_cond_destroy (&ctx->cv); |
595 | pthread_cond_signal (&ctx->cv); |
|
|
596 | pthread_mutex_unlock (&coro_mutex); |
605 | } |
597 | } |
606 | |
598 | |
607 | /*****************************************************************************/ |
599 | /*****************************************************************************/ |
608 | /* fiber backend */ |
600 | /* fiber backend */ |
609 | /*****************************************************************************/ |
601 | /*****************************************************************************/ |