/* fiber.c - fiber management * * Copyright (C) 2009 Timo Teräs * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 or later as * published by the Free Software Foundation. * * See http://www.gnu.org/ for details. */ #include #include #include #include #include #include #include "uctx.h" #define TF_FIBERF_RUNNING TF_BIT(0) #define TF_FIBERF_WAKEUP_PENDING TF_BIT(1) static void tf_fiber_main(void *user_data, void *arg) { tf_fiber_proc proc = (tf_fiber_proc) arg; struct tf_fiber *f = user_data; proc(f->data); tf_fiber_exit(); } void *tf_fiber_create(tf_fiber_proc fiber_main, int private_size) { struct tf_fiber *self = tf_vmach_get_current_fiber(); struct tf_fiber *f; f = tf_uctx_create_embedded( TF_STACK_SIZE, sizeof(struct tf_fiber) + private_size, offsetof(struct tf_fiber, context), tf_fiber_main, fiber_main); if (f == NULL) return NULL; *f = (struct tf_fiber) { .ref_count = 1, .queue_node = TF_LIST_INITIALIZER(f->queue_node), .context = f->context, .timeout.manager = self ? self->timeout.manager : NULL, .wakeup_q = TF_LIST_HEAD_INITIALIZER(f->wakeup_q), }; return f->data; } int tf_fiber_run(void *fiber) { struct tf_timeout_manager *tm; struct tf_fiber *f = container_of(fiber, struct tf_fiber, data); tm = f->timeout.manager; if (tm != NULL) { if (tf_heap_prealloc(&tm->heap, tm->num_fibers + 1) < 0) return -ENOMEM; tm->num_fibers++; } tf_fiber_wakeup(f); tf_vmach_get_current()->num_user_fibers++; return 0; } void *tf_fiber_get(void *data) { struct tf_fiber *fiber = container_of(data, struct tf_fiber, data); tf_atomic_inc(fiber->ref_count); return data; } static void __tf_fiber_destroy(struct tf_fiber *fiber) { tf_heap_delete(&fiber->timeout.heap_node, &fiber->timeout.manager->heap); tf_uctx_destroy(fiber->context); } void tf_fiber_put(void *data) { struct tf_fiber *fiber = container_of(data, struct tf_fiber, data); if (tf_atomic_dec(fiber->ref_count) == 0) __tf_fiber_destroy(fiber); } void tf_fiber_wakeup(struct tf_fiber *f) { struct tf_fiber *self = tf_vmach_get_current_fiber(); unsigned int newval, oldval; do { oldval = f->flags; if (oldval & TF_FIBERF_WAKEUP_PENDING) return; newval = oldval | TF_FIBERF_WAKEUP_PENDING | TF_FIBERF_RUNNING; } while (!tf_atomic_cmpxchg(&f->flags, oldval, newval)); if (!(oldval & TF_FIBERF_RUNNING)) tf_list_add_tail(&f->queue_node, &self->wakeup_q); } int tf_fiber_schedule(void) { struct tf_fiber *f = tf_vmach_get_current_fiber(); if (f->timeout.value != 0 && tf_mtime_diff(f->timeout.value, tf_mtime_now()) <= 0) return -ETIME; if (f->flags & TF_FIBERF_WAKEUP_PENDING) { f->flags = TF_FIBERF_RUNNING; return 0; } if (f->timeout.value != f->timeout.latched) tf_timeout_adjust(&f->timeout); f->flags = 0; tf_uctx_transfer(f->context, f->return_context); f->flags = TF_FIBERF_RUNNING; if (f->timeout.value != 0 && tf_mtime_diff(f->timeout.value, tf_mtime_now()) <= 0) return -ETIME; return 0; } void tf_fiber_exit(void) { struct tf_fiber *f = tf_vmach_get_current_fiber(); if (f->timeout.manager != NULL) tf_timeout_delete(&f->timeout); tf_vmach_get_current()->num_user_fibers--; tf_uctx_transfer(f->context, f->return_context); TF_BUG_ON(1); } void tf_fiber_kill(void *fiber) { }