12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534 |
- /*
- 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 hte 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;
- /**
- * 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 (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)
- {
- unsigned int i;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "destroying task %p\n",
- t);
- if (GNUNET_YES == t->own_handles)
- {
- for (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 ((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)
- {
- struct GNUNET_SCHEDULER_Task *pos;
- 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 (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;
- /* 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;
- /* 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);
- init_backtrace (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)
- {
- return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO,
- task,
- task_cls);
- }
- /**
- * 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)
- {
- enum GNUNET_SCHEDULER_Priority p;
- 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 (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
- {
- pos = ready_head[p];
- 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[p]))
- {
- GNUNET_CONTAINER_DLL_remove (ready_head[p],
- ready_tail[p],
- 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_NO,
- GNUNET_NO,
- GNUNET_NO,
- GNUNET_NO);
- 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;
- install_parent_control_task =
- GNUNET_SCHEDULER_add_now (&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))
- {
- 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);
- }
- }
- struct GNUNET_TIME_Relative time_remaining =
- GNUNET_TIME_absolute_get_remaining (context->timeout);
- 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 */
|