1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569 |
- /*
- This file is part of GNUnet
- Copyright (C) 2009-2017 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file util/scheduler.c
- * @brief schedule computations using continuation passing style
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_util_lib.h"
- #include "disk.h"
- // DEBUG
- #include <inttypes.h>
- #define LOG(kind, ...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
- #define LOG_STRERROR(kind, syscall) GNUNET_log_from_strerror (kind, \
- "util-scheduler", \
- syscall)
- #if HAVE_EXECINFO_H
- #include "execinfo.h"
- /**
- * Use lsof to generate file descriptor reports on select error?
- * (turn off for stable releases).
- */
- #define USE_LSOF GNUNET_NO
- /**
- * Obtain trace information for all scheduler calls that schedule tasks.
- */
- #define EXECINFO GNUNET_NO
- /**
- * Check each file descriptor before adding
- */
- #define DEBUG_FDS GNUNET_NO
- /**
- * Depth of the traces collected via EXECINFO.
- */
- #define MAX_TRACE_DEPTH 50
- #endif
- /**
- * Should we figure out which tasks are delayed for a while
- * before they are run? (Consider using in combination with EXECINFO).
- */
- #define PROFILE_DELAYS GNUNET_NO
- /**
- * Task that were in the queue for longer than this are reported if
- * PROFILE_DELAYS is active.
- */
- #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
- /**
- * Argument to be passed from the driver to
- * #GNUNET_SCHEDULER_do_work(). Contains the
- * scheduler's internal state.
- */
- struct GNUNET_SCHEDULER_Handle
- {
- /**
- * Passed here to avoid constantly allocating/deallocating
- * this element, but generally we want to get rid of this.
- * @deprecated
- */
- struct GNUNET_NETWORK_FDSet *rs;
- /**
- * Passed here to avoid constantly allocating/deallocating
- * this element, but generally we want to get rid of this.
- * @deprecated
- */
- struct GNUNET_NETWORK_FDSet *ws;
- /**
- * context of the SIGINT handler
- */
- struct GNUNET_SIGNAL_Context *shc_int;
- /**
- * context of the SIGTERM handler
- */
- struct GNUNET_SIGNAL_Context *shc_term;
- #if (SIGTERM != GNUNET_TERM_SIG)
- /**
- * context of the TERM_SIG handler
- */
- struct GNUNET_SIGNAL_Context *shc_gterm;
- #endif
- /**
- * context of the SIGQUIT handler
- */
- struct GNUNET_SIGNAL_Context *shc_quit;
- /**
- * context of the SIGHUP handler
- */
- struct GNUNET_SIGNAL_Context *shc_hup;
- /**
- * context of the SIGPIPE handler
- */
- struct GNUNET_SIGNAL_Context *shc_pipe;
- };
- /**
- * Entry in list of pending tasks.
- */
- struct GNUNET_SCHEDULER_Task
- {
- /**
- * This is a linked list.
- */
- struct GNUNET_SCHEDULER_Task *next;
- /**
- * This is a linked list.
- */
- struct GNUNET_SCHEDULER_Task *prev;
- /**
- * Function to run when ready.
- */
- GNUNET_SCHEDULER_TaskCallback callback;
- /**
- * Closure for the @e callback.
- */
- void *callback_cls;
- /**
- * Information about which FDs are ready for this task (and why).
- */
- struct GNUNET_SCHEDULER_FdInfo *fds;
- /**
- * Storage location used for @e fds if we want to avoid
- * a separate malloc() call in the common case that this
- * task is only about a single FD.
- */
- struct GNUNET_SCHEDULER_FdInfo fdx;
- /**
- * Size of the @e fds array.
- */
- unsigned int fds_len;
- /**
- * Do we own the network and file handles referenced by the FdInfo
- * structs in the fds array. This will only be GNUNET_YES if the
- * task was created by the #GNUNET_SCHEDULER_add_select function.
- */
- int own_handles;
- /**
- * Absolute timeout value for the task, or
- * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
- */
- struct GNUNET_TIME_Absolute timeout;
- #if PROFILE_DELAYS
- /**
- * When was the task scheduled?
- */
- struct GNUNET_TIME_Absolute start_time;
- #endif
- /**
- * Why is the task ready? Set after task is added to ready queue.
- * Initially set to zero. All reasons that have already been
- * satisfied (i.e. read or write ready) will be set over time.
- */
- enum GNUNET_SCHEDULER_Reason reason;
- /**
- * Task priority.
- */
- enum GNUNET_SCHEDULER_Priority priority;
- /**
- * Set if we only wait for reading from a single FD, otherwise -1.
- */
- int read_fd;
- /**
- * Set if we only wait for writing to a single FD, otherwise -1.
- */
- int write_fd;
- /**
- * Should the existence of this task in the queue be counted as
- * reason to not shutdown the scheduler?
- */
- int lifeness;
- /**
- * Is this task run on shutdown?
- */
- int on_shutdown;
- /**
- * Is this task in the ready list?
- */
- int in_ready_list;
- #if EXECINFO
- /**
- * Array of strings which make up a backtrace from the point when this
- * task was scheduled (essentially, who scheduled the task?)
- */
- char **backtrace_strings;
- /**
- * Size of the backtrace_strings array
- */
- int num_backtrace_strings;
- #endif
- /**
- * Asynchronous scope of the task that scheduled this scope,
- */
- struct GNUNET_AsyncScopeSave scope;
- };
- /**
- * A struct representing an event the select driver is waiting for
- */
- struct Scheduled
- {
- struct Scheduled *prev;
- struct Scheduled *next;
- /**
- * the task, the event is related to
- */
- struct GNUNET_SCHEDULER_Task *task;
- /**
- * information about the network socket / file descriptor where
- * the event is expected to occur
- */
- struct GNUNET_SCHEDULER_FdInfo *fdi;
- /**
- * the event types (multiple event types can be ORed) the select
- * driver is expected to wait for
- */
- enum GNUNET_SCHEDULER_EventType et;
- };
- /**
- * Driver context used by GNUNET_SCHEDULER_run
- */
- struct DriverContext
- {
- /**
- * the head of a DLL containing information about the events the
- * select driver is waiting for
- */
- struct Scheduled *scheduled_head;
- /**
- * the tail of a DLL containing information about the events the
- * select driver is waiting for
- */
- struct Scheduled *scheduled_tail;
- /**
- * the time when the select driver will wake up again (after
- * calling select)
- */
- struct GNUNET_TIME_Absolute timeout;
- };
- /**
- * The driver used for the event loop. Will be handed over to
- * the scheduler in #GNUNET_SCHEDULER_do_work(), persisted
- * there in this variable for later use in functions like
- * #GNUNET_SCHEDULER_add_select(), #add_without_sets() and
- * #GNUNET_SCHEDULER_cancel().
- */
- static const struct GNUNET_SCHEDULER_Driver *scheduler_driver;
- /**
- * Head of list of tasks waiting for an event.
- */
- static struct GNUNET_SCHEDULER_Task *pending_head;
- /**
- * Tail of list of tasks waiting for an event.
- */
- static struct GNUNET_SCHEDULER_Task *pending_tail;
- /**
- * Head of list of tasks waiting for shutdown.
- */
- static struct GNUNET_SCHEDULER_Task *shutdown_head;
- /**
- * Tail of list of tasks waiting for shutdown.
- */
- static struct GNUNET_SCHEDULER_Task *shutdown_tail;
- /**
- * List of tasks waiting ONLY for a timeout event.
- * Sorted by timeout (earliest first). Used so that
- * we do not traverse the list of these tasks when
- * building select sets (we just look at the head
- * to determine the respective timeout ONCE).
- */
- static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
- /**
- * List of tasks waiting ONLY for a timeout event.
- * Sorted by timeout (earliest first). Used so that
- * we do not traverse the list of these tasks when
- * building select sets (we just look at the head
- * to determine the respective timeout ONCE).
- */
- static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
- /**
- * Last inserted task waiting ONLY for a timeout event.
- * Used to (heuristically) speed up insertion.
- */
- static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
- /**
- * ID of the task that is running right now.
- */
- static struct GNUNET_SCHEDULER_Task *active_task;
- /**
- * Head of list of tasks ready to run right now, grouped by importance.
- */
- static struct
- GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
- /**
- * Tail of list of tasks ready to run right now, grouped by importance.
- */
- static struct
- GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
- /**
- * Task for installing parent control handlers (it might happen that the
- * scheduler is shutdown before this task is executed, so
- * GNUNET_SCHEDULER_shutdown must cancel it in that case)
- */
- static struct GNUNET_SCHEDULER_Task *install_parent_control_task;
- /**
- * Task for reading from a pipe that signal handlers will use to initiate
- * shutdown
- */
- static struct GNUNET_SCHEDULER_Task *shutdown_pipe_task;
- /**
- * Number of tasks on the ready list.
- */
- static unsigned int ready_count;
- /**
- * Priority of the task running right now. Only
- * valid while a task is running.
- */
- static enum GNUNET_SCHEDULER_Priority current_priority;
- /**
- * Priority of the highest task added in the current select
- * iteration.
- */
- static enum GNUNET_SCHEDULER_Priority max_priority_added;
- /**
- * Value of the 'lifeness' flag for the current task.
- */
- static int current_lifeness;
- /**
- * Priority used currently in #GNUNET_SCHEDULER_do_work().
- */
- static enum GNUNET_SCHEDULER_Priority work_priority;
- /**
- * Function to use as a select() in the scheduler.
- * If NULL, we use GNUNET_NETWORK_socket_select().
- */
- static GNUNET_SCHEDULER_select scheduler_select;
- /**
- * Task context of the current task.
- */
- static struct GNUNET_SCHEDULER_TaskContext tc;
- /**
- * Closure for #scheduler_select.
- */
- static void *scheduler_select_cls;
- /**
- * Sets the select function to use in the scheduler (scheduler_select).
- *
- * @param new_select new select function to use
- * @param new_select_cls closure for @a new_select
- * @return previously used select function, NULL for default
- */
- void
- GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
- void *new_select_cls)
- {
- scheduler_select = new_select;
- scheduler_select_cls = new_select_cls;
- }
- /**
- * Check that the given priority is legal (and return it).
- *
- * @param p priority value to check
- * @return p on success, 0 on error
- */
- static enum GNUNET_SCHEDULER_Priority
- check_priority (enum GNUNET_SCHEDULER_Priority p)
- {
- if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
- return p;
- GNUNET_assert (0);
- return 0; /* make compiler happy */
- }
- /**
- * chooses the nearest timeout from all pending tasks, to be used
- * to tell the driver the next wakeup time (using its set_wakeup
- * callback)
- */
- struct GNUNET_TIME_Absolute
- get_timeout ()
- {
- struct GNUNET_SCHEDULER_Task *pos;
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Absolute timeout;
- pos = pending_timeout_head;
- now = GNUNET_TIME_absolute_get ();
- timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
- if (NULL != pos)
- {
- if (0 != pos->reason)
- {
- return now;
- }
- else
- {
- timeout = pos->timeout;
- }
- }
- for (pos = pending_head; NULL != pos; pos = pos->next)
- {
- if (0 != pos->reason)
- {
- return now;
- }
- else if ((pos->timeout.abs_value_us !=
- GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) &&
- (timeout.abs_value_us > pos->timeout.abs_value_us))
- {
- timeout = pos->timeout;
- }
- }
- return timeout;
- }
- /**
- * Put a task that is ready for execution into the ready queue.
- *
- * @param task task ready for execution
- */
- static void
- queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
- {
- enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
- GNUNET_CONTAINER_DLL_insert_tail (ready_head[p],
- ready_tail[p],
- task);
- task->in_ready_list = GNUNET_YES;
- ready_count++;
- }
- /**
- * Request the shutdown of a scheduler. Marks all tasks
- * awaiting shutdown as ready. Note that tasks
- * scheduled with #GNUNET_SCHEDULER_add_shutdown() AFTER this call
- * will be delayed until the next shutdown signal.
- */
- void
- GNUNET_SCHEDULER_shutdown ()
- {
- struct GNUNET_SCHEDULER_Task *pos;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "GNUNET_SCHEDULER_shutdown\n");
- if (NULL != install_parent_control_task)
- {
- GNUNET_SCHEDULER_cancel (install_parent_control_task);
- install_parent_control_task = NULL;
- }
- if (NULL != shutdown_pipe_task)
- {
- GNUNET_SCHEDULER_cancel (shutdown_pipe_task);
- shutdown_pipe_task = NULL;
- }
- while (NULL != (pos = shutdown_head))
- {
- GNUNET_CONTAINER_DLL_remove (shutdown_head,
- shutdown_tail,
- pos);
- pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
- queue_ready_task (pos);
- }
- }
- /**
- * Output stack trace of task @a t.
- *
- * @param t task to dump stack trace of
- */
- static void
- dump_backtrace (struct GNUNET_SCHEDULER_Task *t)
- {
- #if EXECINFO
- for (unsigned int i = 0; i < t->num_backtrace_strings; i++)
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "Task %p trace %u: %s\n",
- t,
- i,
- t->backtrace_strings[i]);
- #else
- (void) t;
- #endif
- }
- /**
- * Destroy a task (release associated resources)
- *
- * @param t task to destroy
- */
- static void
- destroy_task (struct GNUNET_SCHEDULER_Task *t)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "destroying task %p\n",
- t);
- if (GNUNET_YES == t->own_handles)
- {
- for (unsigned int i = 0; i != t->fds_len; ++i)
- {
- const struct GNUNET_NETWORK_Handle *fd = t->fds[i].fd;
- const struct GNUNET_DISK_FileHandle *fh = t->fds[i].fh;
- if (fd)
- {
- GNUNET_NETWORK_socket_free_memory_only_ (
- (struct GNUNET_NETWORK_Handle *) fd);
- }
- if (fh)
- {
- // FIXME: on WIN32 this is not enough! A function
- // GNUNET_DISK_file_free_memory_only would be nice
- GNUNET_free_nz ((void *) fh);
- }
- }
- }
- if (t->fds_len > 1)
- {
- GNUNET_array_grow (t->fds, t->fds_len, 0);
- }
- #if EXECINFO
- GNUNET_free (t->backtrace_strings);
- #endif
- GNUNET_free (t);
- }
- /**
- * Pipe used to communicate shutdown via signal.
- */
- static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
- /**
- * Process ID of this process at the time we installed the various
- * signal handlers.
- */
- static pid_t my_pid;
- /**
- * Signal handler called for SIGPIPE.
- */
- static void
- sighandler_pipe ()
- {
- return;
- }
- ///**
- // * Wait for a short time.
- // * Sleeps for @a ms ms (as that should be long enough for virtually all
- // * modern systems to context switch and allow another process to do
- // * some 'real' work).
- // *
- // * @param ms how many ms to wait
- // */
- // static void
- // short_wait (unsigned int ms)
- // {
- // struct GNUNET_TIME_Relative timeout;
- //
- // timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
- // (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
- // }
- /**
- * Signal handler called for signals that should cause us to shutdown.
- */
- static void
- sighandler_shutdown ()
- {
- static char c;
- int old_errno = errno; /* backup errno */
- if (getpid () != my_pid)
- _exit (1); /* we have fork'ed since the signal handler was created,
- * ignore the signal, see https://gnunet.org/vfork discussion */
- GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
- (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
- &c, sizeof(c));
- errno = old_errno;
- }
- static void
- shutdown_if_no_lifeness ()
- {
- struct GNUNET_SCHEDULER_Task *t;
- if (ready_count > 0)
- return;
- for (t = pending_head; NULL != t; t = t->next)
- if (GNUNET_YES == t->lifeness)
- return;
- for (t = shutdown_head; NULL != t; t = t->next)
- if (GNUNET_YES == t->lifeness)
- return;
- for (t = pending_timeout_head; NULL != t; t = t->next)
- if (GNUNET_YES == t->lifeness)
- return;
- /* No lifeness! */
- GNUNET_SCHEDULER_shutdown ();
- }
- static int
- select_loop (struct GNUNET_SCHEDULER_Handle *sh,
- struct DriverContext *context);
- /**
- * Initialize and run scheduler. This function will return when all
- * tasks have completed. On systems with signals, receiving a SIGTERM
- * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
- * to be run after the active task is complete. As a result, SIGTERM
- * causes all active tasks to be scheduled with reason
- * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added
- * afterwards will execute normally!). Note that any particular signal
- * will only shut down one scheduler; applications should always only
- * create a single scheduler.
- *
- * @param task task to run immediately
- * @param task_cls closure of @a task
- */
- void
- GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- struct GNUNET_SCHEDULER_Handle *sh;
- struct GNUNET_SCHEDULER_Driver *driver;
- struct DriverContext context = {
- .scheduled_head = NULL,
- .scheduled_tail = NULL,
- .timeout = GNUNET_TIME_absolute_get ()
- };
- driver = GNUNET_SCHEDULER_driver_select ();
- driver->cls = &context;
- sh = GNUNET_SCHEDULER_driver_init (driver);
- GNUNET_SCHEDULER_add_with_reason_and_priority (task,
- task_cls,
- GNUNET_SCHEDULER_REASON_STARTUP,
- GNUNET_SCHEDULER_PRIORITY_DEFAULT);
- select_loop (sh,
- &context);
- GNUNET_SCHEDULER_driver_done (sh);
- GNUNET_free (driver);
- }
- /**
- * Obtain the task context, giving the reason why the current task was
- * started.
- *
- * @return current tasks' scheduler context
- */
- const struct GNUNET_SCHEDULER_TaskContext *
- GNUNET_SCHEDULER_get_task_context ()
- {
- GNUNET_assert (NULL != active_task);
- return &tc;
- }
- /**
- * Get information about the current load of this scheduler. Use this
- * function to determine if an elective task should be added or simply
- * dropped (if the decision should be made based on the number of
- * tasks ready to run).
- *
- * @param p priority level to look at
- * @return number of tasks pending right now
- */
- unsigned int
- GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
- {
- unsigned int ret;
- GNUNET_assert (NULL != active_task);
- if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
- return ready_count;
- if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
- p = current_priority;
- ret = 0;
- for (struct GNUNET_SCHEDULER_Task *pos = ready_head[check_priority (p)];
- NULL != pos;
- pos = pos->next)
- ret++;
- return ret;
- }
- void
- init_fd_info (struct GNUNET_SCHEDULER_Task *t,
- const struct GNUNET_NETWORK_Handle *const *read_nh,
- unsigned int read_nh_len,
- const struct GNUNET_NETWORK_Handle *const *write_nh,
- unsigned int write_nh_len,
- const struct GNUNET_DISK_FileHandle *const *read_fh,
- unsigned int read_fh_len,
- const struct GNUNET_DISK_FileHandle *const *write_fh,
- unsigned int write_fh_len)
- {
- // FIXME: if we have exactly two network handles / exactly two file handles
- // and they are equal, we can make one FdInfo with both
- // GNUNET_SCHEDULER_ET_IN and GNUNET_SCHEDULER_ET_OUT set.
- struct GNUNET_SCHEDULER_FdInfo *fdi;
- t->fds_len = read_nh_len + write_nh_len + read_fh_len + write_fh_len;
- if (1 == t->fds_len)
- {
- fdi = &t->fdx;
- t->fds = fdi;
- if (1 == read_nh_len)
- {
- GNUNET_assert (NULL != read_nh);
- GNUNET_assert (NULL != *read_nh);
- fdi->fd = *read_nh;
- fdi->et = GNUNET_SCHEDULER_ET_IN;
- fdi->sock = GNUNET_NETWORK_get_fd (*read_nh);
- t->read_fd = fdi->sock;
- t->write_fd = -1;
- }
- else if (1 == write_nh_len)
- {
- GNUNET_assert (NULL != write_nh);
- GNUNET_assert (NULL != *write_nh);
- fdi->fd = *write_nh;
- fdi->et = GNUNET_SCHEDULER_ET_OUT;
- fdi->sock = GNUNET_NETWORK_get_fd (*write_nh);
- t->read_fd = -1;
- t->write_fd = fdi->sock;
- }
- else if (1 == read_fh_len)
- {
- GNUNET_assert (NULL != read_fh);
- GNUNET_assert (NULL != *read_fh);
- fdi->fh = *read_fh;
- fdi->et = GNUNET_SCHEDULER_ET_IN;
- fdi->sock = (*read_fh)->fd; // FIXME: does not work under WIN32
- t->read_fd = fdi->sock;
- t->write_fd = -1;
- }
- else
- {
- GNUNET_assert (NULL != write_fh);
- GNUNET_assert (NULL != *write_fh);
- fdi->fh = *write_fh;
- fdi->et = GNUNET_SCHEDULER_ET_OUT;
- fdi->sock = (*write_fh)->fd; // FIXME: does not work under WIN32
- t->read_fd = -1;
- t->write_fd = fdi->sock;
- }
- }
- else
- {
- fdi = GNUNET_new_array (t->fds_len, struct GNUNET_SCHEDULER_FdInfo);
- t->fds = fdi;
- t->read_fd = -1;
- t->write_fd = -1;
- unsigned int i;
- for (i = 0; i != read_nh_len; ++i)
- {
- fdi->fd = read_nh[i];
- GNUNET_assert (NULL != fdi->fd);
- fdi->et = GNUNET_SCHEDULER_ET_IN;
- fdi->sock = GNUNET_NETWORK_get_fd (read_nh[i]);
- ++fdi;
- }
- for (i = 0; i != write_nh_len; ++i)
- {
- fdi->fd = write_nh[i];
- GNUNET_assert (NULL != fdi->fd);
- fdi->et = GNUNET_SCHEDULER_ET_OUT;
- fdi->sock = GNUNET_NETWORK_get_fd (write_nh[i]);
- ++fdi;
- }
- for (i = 0; i != read_fh_len; ++i)
- {
- fdi->fh = read_fh[i];
- GNUNET_assert (NULL != fdi->fh);
- fdi->et = GNUNET_SCHEDULER_ET_IN;
- fdi->sock = (read_fh[i])->fd; // FIXME: does not work under WIN32
- ++fdi;
- }
- for (i = 0; i != write_fh_len; ++i)
- {
- fdi->fh = write_fh[i];
- GNUNET_assert (NULL != fdi->fh);
- fdi->et = GNUNET_SCHEDULER_ET_OUT;
- fdi->sock = (write_fh[i])->fd; // FIXME: does not work under WIN32
- ++fdi;
- }
- }
- }
- /**
- * calls the given function @a func on each FdInfo related to @a t.
- * Optionally updates the event type field in each FdInfo after calling
- * @a func.
- *
- * @param t the task
- * @param driver_func the function to call with each FdInfo contained in
- * in @a t
- * @param if_not_ready only call @a driver_func on FdInfos that are not
- * ready
- * @param et the event type to be set in each FdInfo after calling
- * @a driver_func on it, or -1 if no updating not desired.
- */
- static void
- driver_add_multiple (struct GNUNET_SCHEDULER_Task *t)
- {
- struct GNUNET_SCHEDULER_FdInfo *fdi;
- int success = GNUNET_YES;
- for (unsigned int i = 0; i != t->fds_len; ++i)
- {
- fdi = &t->fds[i];
- success = scheduler_driver->add (scheduler_driver->cls,
- t,
- fdi) && success;
- fdi->et = GNUNET_SCHEDULER_ET_NONE;
- }
- if (GNUNET_YES != success)
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "driver could not add task\n");
- }
- }
- static void
- install_parent_control_handler (void *cls)
- {
- (void) cls;
- install_parent_control_task = NULL;
- GNUNET_OS_install_parent_control_handler (NULL);
- }
- static void
- shutdown_pipe_cb (void *cls)
- {
- char c;
- const struct GNUNET_DISK_FileHandle *pr;
- (void) cls;
- shutdown_pipe_task = NULL;
- pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
- GNUNET_DISK_PIPE_END_READ);
- GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
- /* consume the signal */
- GNUNET_DISK_file_read (pr, &c, sizeof(c));
- /* mark all active tasks as ready due to shutdown */
- GNUNET_SCHEDULER_shutdown ();
- shutdown_pipe_task =
- GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
- pr,
- &shutdown_pipe_cb,
- NULL);
- }
- /**
- * Cancel the task with the specified identifier.
- * The task must not yet have run. Only allowed to be called as long as the
- * scheduler is running, that is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param task id of the task to cancel
- * @return original closure of the task
- */
- void *
- GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
- {
- enum GNUNET_SCHEDULER_Priority p;
- int is_fd_task;
- void *ret;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "canceling task %p\n",
- task);
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- is_fd_task = (NULL != task->fds);
- if (is_fd_task)
- {
- int del_result = scheduler_driver->del (scheduler_driver->cls, task);
- if (GNUNET_OK != del_result)
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "driver could not delete task\n");
- GNUNET_assert (0);
- }
- }
- if (! task->in_ready_list)
- {
- if (is_fd_task)
- {
- GNUNET_CONTAINER_DLL_remove (pending_head,
- pending_tail,
- task);
- }
- else if (GNUNET_YES == task->on_shutdown)
- {
- GNUNET_CONTAINER_DLL_remove (shutdown_head,
- shutdown_tail,
- task);
- }
- else
- {
- GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
- pending_timeout_tail,
- task);
- if (pending_timeout_last == task)
- pending_timeout_last = NULL;
- }
- }
- else
- {
- p = check_priority (task->priority);
- GNUNET_CONTAINER_DLL_remove (ready_head[p],
- ready_tail[p],
- task);
- ready_count--;
- }
- ret = task->callback_cls;
- destroy_task (task);
- return ret;
- }
- /**
- * Initialize backtrace data for task @a t
- *
- * @param t task to initialize
- */
- static void
- init_backtrace (struct GNUNET_SCHEDULER_Task *t)
- {
- #if EXECINFO
- void *backtrace_array[MAX_TRACE_DEPTH];
- t->num_backtrace_strings
- = backtrace (backtrace_array, MAX_TRACE_DEPTH);
- t->backtrace_strings =
- backtrace_symbols (backtrace_array,
- t->num_backtrace_strings);
- dump_backtrace (t);
- #else
- (void) t;
- #endif
- }
- /**
- * Continue the current execution with the given function. This is
- * similar to the other "add" functions except that there is no delay
- * and the reason code can be specified.
- *
- * @param task main function of the task
- * @param task_cls closure for @a task
- * @param reason reason for task invocation
- * @param priority priority to use for the task
- */
- void
- GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback
- task,
- void *task_cls,
- enum GNUNET_SCHEDULER_Reason
- reason,
- enum GNUNET_SCHEDULER_Priority
- priority)
- {
- struct GNUNET_SCHEDULER_Task *t;
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- GNUNET_assert (NULL != task);
- t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
- t->read_fd = -1;
- t->write_fd = -1;
- t->callback = task;
- t->callback_cls = task_cls;
- #if PROFILE_DELAYS
- t->start_time = GNUNET_TIME_absolute_get ();
- #endif
- t->reason = reason;
- t->priority = check_priority (priority);
- t->lifeness = current_lifeness;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Adding continuation task %p\n",
- t);
- init_backtrace (t);
- queue_ready_task (t);
- }
- /**
- * Schedule a new task to be run at the specified time. The task
- * will be scheduled for execution at time @a at.
- *
- * @param at time when the operation should run
- * @param priority priority to use for the task
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
- enum GNUNET_SCHEDULER_Priority priority,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- struct GNUNET_SCHEDULER_Task *t;
- struct GNUNET_SCHEDULER_Task *pos;
- struct GNUNET_SCHEDULER_Task *prev;
- struct GNUNET_TIME_Relative left;
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- GNUNET_assert (NULL != task);
- t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
- GNUNET_async_scope_get (&t->scope);
- t->callback = task;
- t->callback_cls = task_cls;
- t->read_fd = -1;
- t->write_fd = -1;
- #if PROFILE_DELAYS
- t->start_time = GNUNET_TIME_absolute_get ();
- #endif
- t->timeout = at;
- t->priority = check_priority (priority);
- t->lifeness = current_lifeness;
- init_backtrace (t);
- left = GNUNET_TIME_absolute_get_remaining (at);
- if (0 == left.rel_value_us)
- {
- queue_ready_task (t);
- if (priority > work_priority)
- work_priority = priority;
- return t;
- }
- /* try tail first (optimization in case we are
- * appending to a long list of tasks with timeouts) */
- if ((NULL == pending_timeout_head) ||
- (at.abs_value_us < pending_timeout_head->timeout.abs_value_us))
- {
- GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
- pending_timeout_tail,
- t);
- }
- else
- {
- /* first move from heuristic start backwards to before start time */
- prev = pending_timeout_last;
- while ((NULL != prev) &&
- (prev->timeout.abs_value_us > t->timeout.abs_value_us))
- prev = prev->prev;
- /* now, move from heuristic start (or head of list) forward to insertion point */
- if (NULL == prev)
- pos = pending_timeout_head;
- else
- pos = prev->next;
- while ((NULL != pos) && (pos->timeout.abs_value_us <=
- t->timeout.abs_value_us))
- {
- prev = pos;
- pos = pos->next;
- }
- GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
- pending_timeout_tail,
- prev,
- t);
- }
- /* finally, update heuristic insertion point to last insertion... */
- pending_timeout_last = t;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Adding task %p\n",
- t);
- return t;
- }
- /**
- * Schedule a new task to be run with a specified delay. The task
- * will be scheduled for execution once the delay has expired.
- *
- * @param delay when should this operation time out?
- * @param priority priority to use for the task
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
- enum GNUNET_SCHEDULER_Priority
- priority,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_at_with_priority (
- GNUNET_TIME_relative_to_absolute (delay),
- priority,
- task,
- task_cls);
- }
- /**
- * Schedule a new task to be run with a specified priority.
- *
- * @param prio how important is the new task?
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
- prio,
- task,
- task_cls);
- }
- /**
- * Schedule a new task to be run at the specified time. The task
- * will be scheduled for execution once specified time has been
- * reached. It will be run with the DEFAULT priority.
- *
- * @param at time at which this operation should run
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_at_with_priority (at,
- GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- task,
- task_cls);
- }
- /**
- * Schedule a new task to be run with a specified delay. The task
- * will be scheduled for execution once the delay has expired. It
- * will be run with the DEFAULT priority.
- *
- * @param delay when should this operation time out?
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
- GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- task,
- task_cls);
- }
- /**
- * Schedule a new task to be run as soon as possible. Note that this
- * does not guarantee that this will be the next task that is being
- * run, as other tasks with higher priority (or that are already ready
- * to run) might get to run first. Just as with delays, clients must
- * not rely on any particular order of execution between tasks
- * scheduled concurrently.
- *
- * The task will be run with the DEFAULT priority.
- *
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- struct GNUNET_SCHEDULER_Task *t;
- t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
- GNUNET_async_scope_get (&t->scope);
- t->callback = task;
- t->callback_cls = task_cls;
- t->read_fd = -1;
- t->write_fd = -1;
- #if PROFILE_DELAYS
- t->start_time = GNUNET_TIME_absolute_get ();
- #endif
- t->timeout = GNUNET_TIME_UNIT_ZERO_ABS;
- t->priority = current_priority;
- t->on_shutdown = GNUNET_YES;
- t->lifeness = current_lifeness;
- queue_ready_task (t);
- init_backtrace (t);
- return t;
- }
- /**
- * Schedule a new task to be run on shutdown, that is when a CTRL-C
- * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
- * invoked.
- *
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- struct GNUNET_SCHEDULER_Task *t;
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- GNUNET_assert (NULL != task);
- t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
- GNUNET_async_scope_get (&t->scope);
- t->callback = task;
- t->callback_cls = task_cls;
- t->read_fd = -1;
- t->write_fd = -1;
- #if PROFILE_DELAYS
- t->start_time = GNUNET_TIME_absolute_get ();
- #endif
- t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
- t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
- t->on_shutdown = GNUNET_YES;
- t->lifeness = GNUNET_NO;
- GNUNET_CONTAINER_DLL_insert (shutdown_head,
- shutdown_tail,
- t);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Adding shutdown task %p\n",
- t);
- init_backtrace (t);
- return t;
- }
- /**
- * Schedule a new task to be run as soon as possible with the
- * (transitive) ignore-shutdown flag either explicitly set or
- * explicitly enabled. This task (and all tasks created from it,
- * other than by another call to this function) will either count or
- * not count for the "lifeness" of the process. This API is only
- * useful in a few special cases.
- *
- * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- struct GNUNET_SCHEDULER_Task *ret;
- ret = GNUNET_SCHEDULER_add_now (task, task_cls);
- ret->lifeness = lifeness;
- return ret;
- }
- #if DEBUG_FDS
- /**
- * check a raw file descriptor and abort if it is bad (for debugging purposes)
- *
- * @param t the task related to the file descriptor
- * @param raw_fd the raw file descriptor to check
- */
- void
- check_fd (struct GNUNET_SCHEDULER_Task *t, int raw_fd)
- {
- if (-1 != raw_fd)
- {
- int flags = fcntl (raw_fd, F_GETFD);
- if ((flags == -1) && (errno == EBADF))
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "Got invalid file descriptor %d!\n",
- raw_fd);
- init_backtrace (t);
- GNUNET_assert (0);
- }
- }
- }
- #endif
- /**
- * Schedule a new task to be run with a specified delay or when any of
- * the specified file descriptor sets is ready. The delay can be used
- * as a timeout on the socket(s) being ready. The task will be
- * scheduled for execution once either the delay has expired or any of
- * the socket operations is ready. This is the most general
- * function of the "add" family. Note that the "prerequisite_task"
- * must be satisfied in addition to any of the other conditions. In
- * other words, the task will be started when
- * <code>
- * (prerequisite-run)
- * && (delay-ready
- * || any-rs-ready
- * || any-ws-ready)
- * </code>
- *
- * @param delay how long should we wait?
- * @param priority priority to use
- * @param rfd file descriptor we want to read (can be -1)
- * @param wfd file descriptors we want to write (can be -1)
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- static struct GNUNET_SCHEDULER_Task *
- add_without_sets (struct GNUNET_TIME_Relative delay,
- enum GNUNET_SCHEDULER_Priority priority,
- const struct GNUNET_NETWORK_Handle *read_nh,
- const struct GNUNET_NETWORK_Handle *write_nh,
- const struct GNUNET_DISK_FileHandle *read_fh,
- const struct GNUNET_DISK_FileHandle *write_fh,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- struct GNUNET_SCHEDULER_Task *t;
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- GNUNET_assert (NULL != task);
- t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
- GNUNET_async_scope_get (&t->scope);
- init_fd_info (t,
- &read_nh,
- read_nh ? 1 : 0,
- &write_nh,
- write_nh ? 1 : 0,
- &read_fh,
- read_fh ? 1 : 0,
- &write_fh,
- write_fh ? 1 : 0);
- t->callback = task;
- t->callback_cls = task_cls;
- #if DEBUG_FDS
- check_fd (t, NULL != read_nh ? GNUNET_NETWORK_get_fd (read_nh) : -1);
- check_fd (t, NULL != write_nh ? GNUNET_NETWORK_get_fd (write_nh) : -1);
- check_fd (t, NULL != read_fh ? read_fh->fd : -1);
- check_fd (t, NULL != write_fh ? write_fh->fd : -1);
- #endif
- #if PROFILE_DELAYS
- t->start_time = GNUNET_TIME_absolute_get ();
- #endif
- t->timeout = GNUNET_TIME_relative_to_absolute (delay);
- t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ?
- current_priority : priority);
- t->lifeness = current_lifeness;
- GNUNET_CONTAINER_DLL_insert (pending_head,
- pending_tail,
- t);
- driver_add_multiple (t);
- max_priority_added = GNUNET_MAX (max_priority_added,
- t->priority);
- init_backtrace (t);
- return t;
- }
- /**
- * Schedule a new task to be run with a specified delay or when the
- * specified file descriptor is ready for reading. The delay can be
- * used as a timeout on the socket being ready. The task will be
- * scheduled for execution once either the delay has expired or the
- * socket operation is ready. It will be run with the DEFAULT priority.
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param delay when should this operation time out?
- * @param rfd read file-descriptor
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
- struct GNUNET_NETWORK_Handle *rfd,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
- GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- rfd, task, task_cls);
- }
- /**
- * Schedule a new task to be run with a specified priority and to be
- * run after the specified delay or when the specified file descriptor
- * is ready for reading. The delay can be used as a timeout on the
- * socket being ready. The task will be scheduled for execution once
- * either the delay has expired or the socket operation is ready. It
- * will be run with the DEFAULT priority.
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param delay when should this operation time out?
- * @param priority priority to use for the task
- * @param rfd read file-descriptor
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
- enum GNUNET_SCHEDULER_Priority
- priority,
- struct GNUNET_NETWORK_Handle *rfd,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
- rfd,
- GNUNET_YES,
- GNUNET_NO,
- task, task_cls);
- }
- /**
- * Schedule a new task to be run with a specified delay or when the
- * specified file descriptor is ready for writing. The delay can be
- * used as a timeout on the socket being ready. The task will be
- * scheduled for execution once either the delay has expired or the
- * socket operation is ready. It will be run with the priority of
- * the calling task.
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param delay when should this operation time out?
- * @param wfd write file-descriptor
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
- struct GNUNET_NETWORK_Handle *wfd,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_net_with_priority (delay,
- GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- wfd,
- GNUNET_NO, GNUNET_YES,
- task, task_cls);
- }
- /**
- * Schedule a new task to be run with a specified delay or when the
- * specified file descriptor is ready. The delay can be
- * used as a timeout on the socket being ready. The task will be
- * scheduled for execution once either the delay has expired or the
- * socket operation is ready.
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param delay when should this operation time out?
- * @param priority priority of the task
- * @param fd file-descriptor
- * @param on_read whether to poll the file-descriptor for readability
- * @param on_write whether to poll the file-descriptor for writability
- * @param task main function of the task
- * @param task_cls closure of task
- * @return unique task identifier for the job
- * only valid until "task" is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_net_with_priority (struct GNUNET_TIME_Relative delay,
- enum GNUNET_SCHEDULER_Priority priority,
- struct GNUNET_NETWORK_Handle *fd,
- int on_read,
- int on_write,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- GNUNET_assert (on_read || on_write);
- GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
- return add_without_sets (delay, priority,
- on_read ? fd : NULL,
- on_write ? fd : NULL,
- NULL,
- NULL,
- task, task_cls);
- }
- /**
- * Schedule a new task to be run with a specified delay or when the
- * specified file descriptor is ready for reading. The delay can be
- * used as a timeout on the socket being ready. The task will be
- * scheduled for execution once either the delay has expired or the
- * socket operation is ready. It will be run with the DEFAULT priority.
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param delay when should this operation time out?
- * @param rfd read file-descriptor
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
- const struct GNUNET_DISK_FileHandle *rfd,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_file_with_priority (
- delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- rfd, GNUNET_YES, GNUNET_NO,
- task, task_cls);
- }
- /**
- * Schedule a new task to be run with a specified delay or when the
- * specified file descriptor is ready for writing. The delay can be
- * used as a timeout on the socket being ready. The task will be
- * scheduled for execution once either the delay has expired or the
- * socket operation is ready. It will be run with the DEFAULT priority.
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param delay when should this operation time out?
- * @param wfd write file-descriptor
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
- const struct GNUNET_DISK_FileHandle *wfd,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- return GNUNET_SCHEDULER_add_file_with_priority (
- delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- wfd, GNUNET_NO, GNUNET_YES,
- task, task_cls);
- }
- /**
- * Schedule a new task to be run with a specified delay or when the
- * specified file descriptor is ready. The delay can be
- * used as a timeout on the socket being ready. The task will be
- * scheduled for execution once either the delay has expired or the
- * socket operation is ready.
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param delay when should this operation time out?
- * @param priority priority of the task
- * @param fd file-descriptor
- * @param on_read whether to poll the file-descriptor for readability
- * @param on_write whether to poll the file-descriptor for writability
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
- enum GNUNET_SCHEDULER_Priority
- priority,
- const struct
- GNUNET_DISK_FileHandle *fd,
- int on_read, int on_write,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- GNUNET_assert (on_read || on_write);
- GNUNET_assert (fd->fd >= 0);
- return add_without_sets (delay, priority,
- NULL,
- NULL,
- on_read ? fd : NULL,
- on_write ? fd : NULL,
- task, task_cls);
- }
- void
- extract_handles (const struct GNUNET_NETWORK_FDSet *fdset,
- const struct GNUNET_NETWORK_Handle ***ntarget,
- unsigned int *extracted_nhandles,
- const struct GNUNET_DISK_FileHandle ***ftarget,
- unsigned int *extracted_fhandles)
- {
- // FIXME: this implementation only works for unix, for WIN32 the file handles
- // in fdset must be handled separately
- const struct GNUNET_NETWORK_Handle **nhandles;
- const struct GNUNET_DISK_FileHandle **fhandles;
- unsigned int nhandles_len;
- unsigned int fhandles_len;
- nhandles = NULL;
- fhandles = NULL;
- nhandles_len = 0;
- fhandles_len = 0;
- for (int sock = 0; sock != fdset->nsds; ++sock)
- {
- if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (fdset, sock))
- {
- struct GNUNET_NETWORK_Handle *nhandle;
- struct GNUNET_DISK_FileHandle *fhandle;
- nhandle = GNUNET_NETWORK_socket_box_native (sock);
- if (NULL != nhandle)
- {
- GNUNET_array_append (nhandles, nhandles_len, nhandle);
- }
- else
- {
- fhandle = GNUNET_DISK_get_handle_from_int_fd (sock);
- if (NULL != fhandle)
- {
- GNUNET_array_append (fhandles, fhandles_len, fhandle);
- }
- else
- {
- GNUNET_assert (0);
- }
- }
- }
- }
- *ntarget = nhandles_len > 0 ? nhandles : NULL;
- *ftarget = fhandles_len > 0 ? fhandles : NULL;
- *extracted_nhandles = nhandles_len;
- *extracted_fhandles = fhandles_len;
- }
- /**
- * Schedule a new task to be run with a specified delay or when any of
- * the specified file descriptor sets is ready. The delay can be used
- * as a timeout on the socket(s) being ready. The task will be
- * scheduled for execution once either the delay has expired or any of
- * the socket operations is ready. This is the most general
- * function of the "add" family. Note that the "prerequisite_task"
- * must be satisfied in addition to any of the other conditions. In
- * other words, the task will be started when
- * <code>
- * (prerequisite-run)
- * && (delay-ready
- * || any-rs-ready
- * || any-ws-ready) )
- * </code>
- * Only allowed to be called as long as the scheduler is running, that
- * is one of the following conditions is met:
- *
- * - #GNUNET_SCHEDULER_run has been called and has not returned yet
- * - #GNUNET_SCHEDULER_driver_init has been run and
- * #GNUNET_SCHEDULER_driver_done has not been called yet
- *
- * @param prio how important is this task?
- * @param delay how long should we wait?
- * @param rs set of file descriptors we want to read (can be NULL)
- * @param ws set of file descriptors we want to write (can be NULL)
- * @param task main function of the task
- * @param task_cls closure of @a task
- * @return unique task identifier for the job
- * only valid until @a task is started!
- */
- struct GNUNET_SCHEDULER_Task *
- GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
- struct GNUNET_TIME_Relative delay,
- const struct GNUNET_NETWORK_FDSet *rs,
- const struct GNUNET_NETWORK_FDSet *ws,
- GNUNET_SCHEDULER_TaskCallback task,
- void *task_cls)
- {
- struct GNUNET_SCHEDULER_Task *t;
- const struct GNUNET_NETWORK_Handle **read_nhandles = NULL;
- const struct GNUNET_NETWORK_Handle **write_nhandles = NULL;
- const struct GNUNET_DISK_FileHandle **read_fhandles = NULL;
- const struct GNUNET_DISK_FileHandle **write_fhandles = NULL;
- unsigned int read_nhandles_len = 0;
- unsigned int write_nhandles_len = 0;
- unsigned int read_fhandles_len = 0;
- unsigned int write_fhandles_len = 0;
- /* scheduler must be running */
- GNUNET_assert (NULL != scheduler_driver);
- GNUNET_assert (NULL != task);
- int no_rs = (NULL == rs);
- int no_ws = (NULL == ws);
- int empty_rs = (NULL != rs) && (0 == rs->nsds);
- int empty_ws = (NULL != ws) && (0 == ws->nsds);
- int no_fds = (no_rs && no_ws) ||
- (empty_rs && empty_ws) ||
- (no_rs && empty_ws) ||
- (no_ws && empty_rs);
- if (! no_fds)
- {
- if (NULL != rs)
- {
- extract_handles (rs,
- &read_nhandles,
- &read_nhandles_len,
- &read_fhandles,
- &read_fhandles_len);
- }
- if (NULL != ws)
- {
- extract_handles (ws,
- &write_nhandles,
- &write_nhandles_len,
- &write_fhandles,
- &write_fhandles_len);
- }
- }
- /**
- * here we consider the case that a GNUNET_NETWORK_FDSet might be empty
- * although its maximum FD number (nsds) is greater than 0. We handle
- * this case gracefully because some libraries such as libmicrohttpd
- * only provide a hint what the maximum FD number in an FD set might be
- * and not the exact FD number (see e.g. gnunet-rest-service.c)
- */int no_fds_extracted = (0 == read_nhandles_len) &&
- (0 == read_fhandles_len) &&
- (0 == write_nhandles_len) &&
- (0 == write_fhandles_len);
- if (no_fds || no_fds_extracted)
- return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
- prio,
- task,
- task_cls);
- t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
- GNUNET_async_scope_get (&t->scope);
- init_fd_info (t,
- read_nhandles,
- read_nhandles_len,
- write_nhandles,
- write_nhandles_len,
- read_fhandles,
- read_fhandles_len,
- write_fhandles,
- write_fhandles_len);
- t->callback = task;
- t->callback_cls = task_cls;
- t->own_handles = GNUNET_YES;
- /* free the arrays of pointers to network / file handles, the actual
- * handles will be freed in destroy_task */
- GNUNET_array_grow (read_nhandles, read_nhandles_len, 0);
- GNUNET_array_grow (write_nhandles, write_nhandles_len, 0);
- GNUNET_array_grow (read_fhandles, read_fhandles_len, 0);
- GNUNET_array_grow (write_fhandles, write_fhandles_len, 0);
- #if PROFILE_DELAYS
- t->start_time = GNUNET_TIME_absolute_get ();
- #endif
- t->timeout = GNUNET_TIME_relative_to_absolute (delay);
- t->priority =
- check_priority ((prio ==
- GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
- prio);
- t->lifeness = current_lifeness;
- GNUNET_CONTAINER_DLL_insert (pending_head,
- pending_tail,
- t);
- driver_add_multiple (t);
- max_priority_added = GNUNET_MAX (max_priority_added,
- t->priority);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Adding task %p\n",
- t);
- init_backtrace (t);
- return t;
- }
- /**
- * Function used by event-loop implementations to signal the scheduler
- * that a particular @a task is ready due to an event specified in the
- * et field of @a fdi.
- *
- * This function will then queue the task to notify the application
- * that the task is ready (with the respective priority).
- *
- * @param task the task that is ready
- * @param fdi information about the related FD
- */
- void
- GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
- struct GNUNET_SCHEDULER_FdInfo *fdi)
- {
- enum GNUNET_SCHEDULER_Reason reason;
- reason = task->reason;
- if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
- (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)))
- reason |= GNUNET_SCHEDULER_REASON_READ_READY;
- if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
- (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)))
- reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
- reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
- task->reason = reason;
- if (GNUNET_NO == task->in_ready_list)
- {
- GNUNET_CONTAINER_DLL_remove (pending_head,
- pending_tail,
- task);
- queue_ready_task (task);
- }
- }
- /**
- * Function called by external event loop implementations to tell the
- * scheduler to run some of the tasks that are ready. Must be called
- * only after #GNUNET_SCHEDULER_driver_init has been called and before
- * #GNUNET_SCHEDULER_driver_done is called.
- * This function may return even though there are tasks left to run
- * just to give other tasks a chance as well. If we return #GNUNET_YES,
- * the event loop implementation should call this function again as
- * soon as possible, while if we return #GNUNET_NO it must block until
- * either the operating system has more work (the scheduler has no more
- * work to do right now) or the timeout set by the scheduler (using the
- * set_wakeup callback) is reached.
- *
- * @param sh scheduler handle that was returned by
- * #GNUNET_SCHEDULER_driver_init
- * @return #GNUNET_YES if there are more tasks that are ready,
- * and thus we would like to run more (yield to avoid
- * blocking other activities for too long) #GNUNET_NO
- * if we are done running tasks (yield to block)
- */
- int
- GNUNET_SCHEDULER_do_work (struct GNUNET_SCHEDULER_Handle *sh)
- {
- struct GNUNET_SCHEDULER_Task *pos;
- struct GNUNET_TIME_Absolute now;
- /* check for tasks that reached the timeout! */
- now = GNUNET_TIME_absolute_get ();
- pos = pending_timeout_head;
- while (NULL != pos)
- {
- struct GNUNET_SCHEDULER_Task *next = pos->next;
- if (now.abs_value_us >= pos->timeout.abs_value_us)
- pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
- if (0 == pos->reason)
- break;
- GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
- pending_timeout_tail,
- pos);
- if (pending_timeout_last == pos)
- pending_timeout_last = NULL;
- queue_ready_task (pos);
- pos = next;
- }
- pos = pending_head;
- while (NULL != pos)
- {
- struct GNUNET_SCHEDULER_Task *next = pos->next;
- if (now.abs_value_us >= pos->timeout.abs_value_us)
- {
- pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
- GNUNET_CONTAINER_DLL_remove (pending_head,
- pending_tail,
- pos);
- queue_ready_task (pos);
- }
- pos = next;
- }
- if (0 == ready_count)
- {
- struct GNUNET_TIME_Absolute timeout = get_timeout ();
- if (timeout.abs_value_us > now.abs_value_us)
- {
- /**
- * The event loop called this function before the current timeout was
- * reached (and no FD tasks are ready). This is acceptable if
- *
- * - the system time was changed while the driver was waiting for
- * the timeout
- * - an external event loop called GNUnet API functions outside of
- * the callbacks called in GNUNET_SCHEDULER_do_work and thus
- * wasn't notified about the new timeout
- *
- * It might also mean we are busy-waiting because of a programming
- * error in the external event loop.
- */LOG (GNUNET_ERROR_TYPE_DEBUG,
- "GNUNET_SCHEDULER_do_work did not find any ready "
- "tasks and timeout has not been reached yet.\n");
- }
- else
- {
- /**
- * the current timeout was reached but no ready tasks were found,
- * internal scheduler error!
- */
- GNUNET_assert (0);
- }
- }
- else
- {
- /* find out which task priority level we are going to
- process this time */
- max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
- GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
- /* yes, p>0 is correct, 0 is "KEEP" which should
- * always be an empty queue (see assertion)! */
- for (work_priority = GNUNET_SCHEDULER_PRIORITY_COUNT - 1;
- work_priority > 0;
- work_priority--)
- {
- pos = ready_head[work_priority];
- if (NULL != pos)
- break;
- }
- GNUNET_assert (NULL != pos); /* ready_count wrong? */
- /* process all tasks at this priority level, then yield */
- while (NULL != (pos = ready_head[work_priority]))
- {
- GNUNET_CONTAINER_DLL_remove (ready_head[work_priority],
- ready_tail[work_priority],
- pos);
- ready_count--;
- current_priority = pos->priority;
- current_lifeness = pos->lifeness;
- active_task = pos;
- #if PROFILE_DELAYS
- if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
- DELAY_THRESHOLD.rel_value_us)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Task %p took %s to be scheduled\n",
- pos,
- GNUNET_STRINGS_relative_time_to_string (
- GNUNET_TIME_absolute_get_duration (pos->start_time),
- GNUNET_YES));
- }
- #endif
- tc.reason = pos->reason;
- GNUNET_NETWORK_fdset_zero (sh->rs);
- GNUNET_NETWORK_fdset_zero (sh->ws);
- // FIXME: do we have to remove FdInfos from fds if they are not ready?
- tc.fds_len = pos->fds_len;
- tc.fds = pos->fds;
- for (unsigned int i = 0; i != pos->fds_len; ++i)
- {
- struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
- if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
- {
- GNUNET_NETWORK_fdset_set_native (sh->rs,
- fdi->sock);
- }
- if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
- {
- GNUNET_NETWORK_fdset_set_native (sh->ws,
- fdi->sock);
- }
- }
- tc.read_ready = sh->rs;
- tc.write_ready = sh->ws;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Running task %p\n",
- pos);
- GNUNET_assert (NULL != pos->callback);
- {
- struct GNUNET_AsyncScopeSave old_scope;
- if (pos->scope.have_scope)
- GNUNET_async_scope_enter (&pos->scope.scope_id, &old_scope);
- else
- GNUNET_async_scope_get (&old_scope);
- pos->callback (pos->callback_cls);
- GNUNET_async_scope_restore (&old_scope);
- }
- if (NULL != pos->fds)
- {
- int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
- if (GNUNET_OK != del_result)
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "driver could not delete task %p\n", pos);
- GNUNET_assert (0);
- }
- }
- active_task = NULL;
- dump_backtrace (pos);
- destroy_task (pos);
- }
- }
- shutdown_if_no_lifeness ();
- if (0 == ready_count)
- {
- scheduler_driver->set_wakeup (scheduler_driver->cls,
- get_timeout ());
- return GNUNET_NO;
- }
- scheduler_driver->set_wakeup (scheduler_driver->cls,
- GNUNET_TIME_absolute_get ());
- return GNUNET_YES;
- }
- /**
- * Function called by external event loop implementations to initialize
- * the scheduler. An external implementation has to provide @a driver
- * which contains callbacks for the scheduler (see definition of struct
- * #GNUNET_SCHEDULER_Driver). The callbacks are used to instruct the
- * external implementation to watch for events. If it detects any of
- * those events it is expected to call #GNUNET_SCHEDULER_do_work to let
- * the scheduler handle it. If an event is related to a specific task
- * (e.g. the scheduler gave instructions to watch a file descriptor),
- * the external implementation is expected to mark that task ready
- * before by calling #GNUNET_SCHEDULER_task_ready.
- * This function has to be called before any tasks are scheduled and
- * before GNUNET_SCHEDULER_do_work is called for the first time. It
- * allocates resources that have to be freed again by calling
- * #GNUNET_SCHEDULER_driver_done.
- *
- * This function installs the same signal handlers as
- * #GNUNET_SCHEDULER_run. This means SIGTERM (and other similar signals)
- * will induce a call to #GNUNET_SCHEDULER_shutdown during the next
- * call to #GNUNET_SCHEDULER_do_work. As a result, SIGTERM causes all
- * active tasks to be scheduled with reason
- * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added afterwards
- * will execute normally!). Note that any particular signal will only
- * shut down one scheduler; applications should always only create a
- * single scheduler.
- *
- * @param driver to use for the event loop
- * @return handle to be passed to #GNUNET_SCHEDULER_do_work and
- * #GNUNET_SCHEDULER_driver_done
- */
- struct GNUNET_SCHEDULER_Handle *
- GNUNET_SCHEDULER_driver_init (const struct GNUNET_SCHEDULER_Driver *driver)
- {
- struct GNUNET_SCHEDULER_Handle *sh;
- const struct GNUNET_DISK_FileHandle *pr;
- /* scheduler must not be running */
- GNUNET_assert (NULL == scheduler_driver);
- GNUNET_assert (NULL == shutdown_pipe_handle);
- /* general set-up */
- sh = GNUNET_new (struct GNUNET_SCHEDULER_Handle);
- shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
- GNUNET_assert (NULL != shutdown_pipe_handle);
- pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
- GNUNET_DISK_PIPE_END_READ);
- my_pid = getpid ();
- scheduler_driver = driver;
- /* install signal handlers */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Registering signal handlers\n");
- sh->shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
- &sighandler_shutdown);
- sh->shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
- &sighandler_shutdown);
- #if (SIGTERM != GNUNET_TERM_SIG)
- sh->shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
- &sighandler_shutdown);
- #endif
- sh->shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
- &sighandler_pipe);
- sh->shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
- &sighandler_shutdown);
- sh->shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
- &sighandler_shutdown);
- /* Setup initial tasks */
- current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
- current_lifeness = GNUNET_NO;
- /* ensure this task runs first, by using a priority level reserved for
- the scheduler (not really shutdown, but start-up ;-) */
- install_parent_control_task =
- GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_SHUTDOWN,
- &install_parent_control_handler,
- NULL);
- shutdown_pipe_task =
- GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
- pr,
- &shutdown_pipe_cb,
- NULL);
- current_lifeness = GNUNET_YES;
- scheduler_driver->set_wakeup (scheduler_driver->cls,
- get_timeout ());
- /* begin main event loop */
- sh->rs = GNUNET_NETWORK_fdset_create ();
- sh->ws = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_handle_set (sh->rs, pr);
- return sh;
- }
- /**
- * Counter-part of #GNUNET_SCHEDULER_driver_init. Has to be called
- * by external event loop implementations after the scheduler has
- * shut down. This is the case if both of the following conditions
- * are met:
- *
- * - all tasks the scheduler has added through the driver's add
- * callback have been removed again through the driver's del
- * callback
- * - the timeout the scheduler has set through the driver's
- * add_wakeup callback is FOREVER
- *
- * @param sh the handle returned by #GNUNET_SCHEDULER_driver_init
- */
- void
- GNUNET_SCHEDULER_driver_done (struct GNUNET_SCHEDULER_Handle *sh)
- {
- GNUNET_assert (NULL == pending_head);
- GNUNET_assert (NULL == pending_timeout_head);
- GNUNET_assert (NULL == shutdown_head);
- for (int i = 0; i != GNUNET_SCHEDULER_PRIORITY_COUNT; ++i)
- {
- GNUNET_assert (NULL == ready_head[i]);
- }
- GNUNET_NETWORK_fdset_destroy (sh->rs);
- GNUNET_NETWORK_fdset_destroy (sh->ws);
- /* uninstall signal handlers */
- GNUNET_SIGNAL_handler_uninstall (sh->shc_int);
- GNUNET_SIGNAL_handler_uninstall (sh->shc_term);
- #if (SIGTERM != GNUNET_TERM_SIG)
- GNUNET_SIGNAL_handler_uninstall (sh->shc_gterm);
- #endif
- GNUNET_SIGNAL_handler_uninstall (sh->shc_pipe);
- GNUNET_SIGNAL_handler_uninstall (sh->shc_quit);
- GNUNET_SIGNAL_handler_uninstall (sh->shc_hup);
- GNUNET_DISK_pipe_close (shutdown_pipe_handle);
- shutdown_pipe_handle = NULL;
- scheduler_driver = NULL;
- GNUNET_free (sh);
- }
- static int
- select_loop (struct GNUNET_SCHEDULER_Handle *sh,
- struct DriverContext *context)
- {
- struct GNUNET_NETWORK_FDSet *rs;
- struct GNUNET_NETWORK_FDSet *ws;
- int select_result;
- GNUNET_assert (NULL != context);
- rs = GNUNET_NETWORK_fdset_create ();
- ws = GNUNET_NETWORK_fdset_create ();
- while ((NULL != context->scheduled_head) ||
- (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
- context->timeout.abs_value_us))
- {
- struct GNUNET_TIME_Relative time_remaining;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "select timeout = %s\n",
- GNUNET_STRINGS_absolute_time_to_string (context->timeout));
- GNUNET_NETWORK_fdset_zero (rs);
- GNUNET_NETWORK_fdset_zero (ws);
- for (struct Scheduled *pos = context->scheduled_head;
- NULL != pos;
- pos = pos->next)
- {
- if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
- {
- GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
- }
- if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
- {
- GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
- }
- }
- time_remaining = GNUNET_TIME_absolute_get_remaining (context->timeout);
- if (0 < ready_count)
- time_remaining = GNUNET_TIME_UNIT_ZERO;
- if (NULL == scheduler_select)
- {
- select_result = GNUNET_NETWORK_socket_select (rs,
- ws,
- NULL,
- time_remaining);
- }
- else
- {
- select_result = scheduler_select (scheduler_select_cls,
- rs,
- ws,
- NULL,
- time_remaining);
- }
- if (select_result == GNUNET_SYSERR)
- {
- if (errno == EINTR)
- continue;
- LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
- "select");
- #if USE_LSOF
- char lsof[512];
- snprintf (lsof,
- sizeof(lsof),
- "lsof -p %d",
- getpid ());
- (void) close (1);
- (void) dup2 (2, 1);
- if (0 != system (lsof))
- LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
- "system");
- #endif
- #if DEBUG_FDS
- for (struct Scheduled *s = context->scheduled_head;
- NULL != s;
- s = s->next)
- {
- int flags = fcntl (s->fdi->sock,
- F_GETFD);
- if ((flags == -1) &&
- (EBADF == errno))
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- "Got invalid file descriptor %d!\n",
- s->fdi->sock);
- #if EXECINFO
- dump_backtrace (s->task);
- #endif
- }
- }
- #endif
- GNUNET_assert (0);
- GNUNET_NETWORK_fdset_destroy (rs);
- GNUNET_NETWORK_fdset_destroy (ws);
- return GNUNET_SYSERR;
- }
- if (select_result > 0)
- {
- for (struct Scheduled *pos = context->scheduled_head;
- NULL != pos;
- pos = pos->next)
- {
- int is_ready = GNUNET_NO;
- if ((0 != (GNUNET_SCHEDULER_ET_IN & pos->et)) &&
- (GNUNET_YES ==
- GNUNET_NETWORK_fdset_test_native (rs,
- pos->fdi->sock)) )
- {
- pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
- is_ready = GNUNET_YES;
- }
- if ((0 != (GNUNET_SCHEDULER_ET_OUT & pos->et)) &&
- (GNUNET_YES ==
- GNUNET_NETWORK_fdset_test_native (ws,
- pos->fdi->sock)) )
- {
- pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
- is_ready = GNUNET_YES;
- }
- if (GNUNET_YES == is_ready)
- {
- GNUNET_SCHEDULER_task_ready (pos->task,
- pos->fdi);
- }
- }
- }
- if (GNUNET_YES == GNUNET_SCHEDULER_do_work (sh))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "scheduler has more tasks ready!\n");
- }
- }
- GNUNET_NETWORK_fdset_destroy (rs);
- GNUNET_NETWORK_fdset_destroy (ws);
- return GNUNET_OK;
- }
- static int
- select_add (void *cls,
- struct GNUNET_SCHEDULER_Task *task,
- struct GNUNET_SCHEDULER_FdInfo *fdi)
- {
- struct DriverContext *context = cls;
- GNUNET_assert (NULL != context);
- GNUNET_assert (NULL != task);
- GNUNET_assert (NULL != fdi);
- GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
- 0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
- if (! ((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
- {
- /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
- return GNUNET_SYSERR;
- }
- struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
- scheduled->task = task;
- scheduled->fdi = fdi;
- scheduled->et = fdi->et;
- GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
- context->scheduled_tail,
- scheduled);
- return GNUNET_OK;
- }
- static int
- select_del (void *cls,
- struct GNUNET_SCHEDULER_Task *task)
- {
- struct DriverContext *context;
- struct Scheduled *pos;
- int ret;
- GNUNET_assert (NULL != cls);
- context = cls;
- ret = GNUNET_SYSERR;
- pos = context->scheduled_head;
- while (NULL != pos)
- {
- struct Scheduled *next = pos->next;
- if (pos->task == task)
- {
- GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
- context->scheduled_tail,
- pos);
- GNUNET_free (pos);
- ret = GNUNET_OK;
- }
- pos = next;
- }
- return ret;
- }
- static void
- select_set_wakeup (void *cls,
- struct GNUNET_TIME_Absolute dt)
- {
- struct DriverContext *context = cls;
- GNUNET_assert (NULL != context);
- context->timeout = dt;
- }
- /**
- * Obtain the driver for using select() as the event loop.
- *
- * @return NULL on error
- */
- struct GNUNET_SCHEDULER_Driver *
- GNUNET_SCHEDULER_driver_select ()
- {
- struct GNUNET_SCHEDULER_Driver *select_driver;
- select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
- select_driver->add = &select_add;
- select_driver->del = &select_del;
- select_driver->set_wakeup = &select_set_wakeup;
- return select_driver;
- }
- /**
- * Change the async scope for the currently executing task and (transitively)
- * for all tasks scheduled by the current task after calling this function.
- * Nested tasks can begin their own nested async scope.
- *
- * Once the current task is finished, the async scope ID is reset to
- * its previous value.
- *
- * Must only be called from a running task.
- *
- * @param aid the asynchronous scope id to enter
- */
- void
- GNUNET_SCHEDULER_begin_async_scope (struct GNUNET_AsyncScopeId *aid)
- {
- struct GNUNET_AsyncScopeSave dummy_old_scope;
- GNUNET_assert (NULL != active_task);
- /* Since we're in a task, the context will be automatically
- restored by the scheduler. */
- GNUNET_async_scope_enter (aid, &dummy_old_scope);
- }
- /* end of scheduler.c */
|