ev_default_loop()
Declarations:
EV_API_DECL struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; EV_API_DECL int ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; /* returns true when successful */
Author's note:
/* the default loop is the only one that handles signals and child watchers */ /* you can call this as often as you like */
The default loop is the only loop that handles signals and submonitors and can be invoked at any time.
It acts as a critical step, and the use of libev can be roughly divided into these steps:
1. Initialize struct ev_loop structure by calling ev_default_loop
2. Call ev_io_init to initialize the monitor. This macro (function) mainly calls ev_init and ev_io_set, mainly responsible for binding the monitor and callback functions
3. Call ev_io_start to start the monitor, which adds the monitor's file descriptor (Linux Everything Files) to the loop->anfds structure
4. Call ev_run to wait for the event to trigger
Our previous example had this sentence:
struct ev_loop *main_loop = ev_default_loop(0);//New Drive ev_io_start(main_loop,&server_sock_w);//Bind Monitor to Drive - Register ev_run(main_loop,0);//Usually set to 0, meaning that the drive does not stop until all monitors stop, or you can break manually
This main_loop is a main loop. All the sub-monitors need to be registered in the main loop. Here I call it'drive'. The advantage of using the main loop and sub-loop is that in concurrent programming, the monitors of each loop will not compete and interfere with each other.
This function has two definitions corresponding to different conditions:
#if EV_MULTIPLICITY /* the default loop is the only one that handles signals and child watchers */ /* you can call this as often as you like */ EV_API_DECL struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; #else EV_API_DECL int ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; /* returns true when successful */
For EV_MULTIPLICITY:
#ifndef EV_MULTIPLICITY # define EV_MULTIPLICITY EV_FEATURE_CONFIG #endif
The EV_MULTIPLICITY macro is used to determine whether multiple loops are supported or not. A default loop structure, default_loop_struct, and a pointer to it, ev_default_loop_ptr, are provided.
If multiple loops are supported, default_loop_struct is a static struct ev_loop type structure that contains various members, such as ev_tstamp ev_rt_now;Int pendingpri;Ev_default_loop_ptr, for example, is a pointer to the struct ev_loop type. If multiple loops are not supported, the struct ev_loop structure described above no longer exists, its members are defined as static variables, and ev_default_loop_ptr is only an int variable to indicate whether the "loop" has been initialized successfully.
EV_FEATURE_CONFIG:
#define EV_FEATURE_CONFIG ((EV_FEATURES) & 4) #ifndef EV_FEATURES # if defined __OPTIMIZE_SIZE__ # define EV_FEATURES 0x7c # else # define EV_FEATURES 0x7f # endif #endif
The return values of these two functions are an ev_loop* pointer and an int, which you can imagine how to use in a programming language without a pointer.
Their parameters are the same:
unsigned int flags EV_CPP (= 0);//unsigned int flags = 0, parameter with default value
Function Definition:
#if EV_MULTIPLICITY //Supports multiple loop s ecb_cold struct ev_loop * //Support function return value is ev_loop* #else int //Return value int is not supported #endif ev_default_loop (unsigned int flags) EV_THROW //flags defaults to 0 { if (!ev_default_loop_ptr) //If ev_default_loop_ptr is not defined { #if EV_MULTIPLICITY EV_P = ev_default_loop_ptr = &default_loop_struct; //default_loop_struct is a structure //ev_default_loop_ptr is a structure pointer //# define EV_P struct ev_loop *loop #else ev_default_loop_ptr = 1; #endif loop_init (EV_A_ flags);//Initialization, parameters: struct ev_loop *loop, flags //A subsequent reanalysis of the loop_init() function, which initializes the main loop if (ev_backend (EV_A))//View backend lists and suggestions { #if EV_CHILD_ENABLE //If subprocesses or threads are supported, follow-up analysis of the following functions ev_signal_init (&childev, childcb, SIGCHLD); ev_set_priority (&childev, EV_MAXPRI); ev_signal_start (EV_A_ &childev); ev_unref (EV_A); /* child watcher should not keep loop alive */ #endif } else ev_default_loop_ptr = 0; } return ev_default_loop_ptr;//Return structure pointer }
Structures and structure pointers are defined as follows:
#if EV_MULTIPLICITY //Support multiple loops struct ev_loop { ev_tstamp ev_rt_now;//double #define ev_rt_now ((loop)->ev_rt_now) //These predefinations are for uniform specification writing, where loop s do not exist by themselves, and other global variables ->ev_xxx are for specification writing //Defining the variable ev_rt_now before defining the macro does not replace the previously defined variable in the structure because the scope of the macro is from the beginning to the end of the scope //So what does it mean to define this macro? //This is undoubtedly a member variable in the structure pointer //After defining the structure pointer, we can use the macro definition directly //Instead of using loop->ev_xxx, just keep the code simple #define VAR(name,decl) decl //This macro definition is to simplify the code in ev_vars.h #include "ev_vars.h" //Here ev_vars.h is all a macro definition like VARxxx #undef VAR //Undefine macro for VAR later }; #include "ev_wrap.h" //ev_wrap.h is full of macro definitions like #define XXX ((loop) ->xxx) //In other words, everything in this header file is a variable in the loop structure static struct ev_loop default_loop_struct; EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0; /* needs to be initialised to make it a definition despite extern */ #else EV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */ #define VAR(name,decl) static decl; #include "ev_vars.h" #undef VAR //Without a pointer, there would be no ev_wrap.h static int ev_default_loop_ptr; #endif
#if EV_MULTIPLICITY struct ev_loop; # define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */ # define EV_P_ EV_P, /* a loop as first of multiple parameters */ // Notice the comma',' # define EV_A loop
The main function in this function is loop_init()
loop_init()
Function Definition:
/* initialise a loop structure, must be zero-initialised */ noinline ecb_cold //This heavyweight function is generally not inlined static void loop_init (EV_P_ unsigned int flags) EV_THROW //EV_P_is struct ev_loop *loop, { if (!backend) //#define backend ((loop)->backend) { origflags = flags; //#define origflags ((loop)->origflags) //Macro definitions are used so much that if else statements can be cumbersome. Why? Because macro definitions are precompiled, they are faster? #if EV_USE_REALTIME //calendar time //#ifndef CLOCK_REALTIME //# undef EV_USE_REALTIME //# define EV_USE_REALTIME 0 //#endif if (!have_realtime) { struct timespec ts; //Structures in standard header file time.h if (!clock_gettime (CLOCK_REALTIME, &ts)) //# define clock_gettime(id, ts) syscall (SYS_clock_gettime, (id), (ts)) //syscall system call //CLOCK_REALTIME System Call Parameters have_realtime = 1; //static EV_ATOMIC_T have_realtime; //# define EV_ATOMIC_T sig_atomic_t volatile //typedef int sig_atomic_t; // volatile keyword: Ensure that instructions are not omitted for optimization } #endif #if EV_USE_MONOTONIC //System Startup Time if (!have_monotonic) { struct timespec ts; if (!clock_gettime (CLOCK_MONOTONIC, &ts)) have_monotonic = 1; } #endif /* pid check not overridable via env */ //pid check cannot be overwritten by env #ifndef _WIN32 if (flags & EVFLAG_FORKCHECK) curpid = getpid (); //#define curpid ((loop)->curpid) //EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */Fork in the middle of each iteration //getpid() system call to get the process identifier, which many programs use to create temporary files to avoid problems caused by the same temporary files #endif if (!(flags & EVFLAG_NOENV) && !enable_secure () && getenv ("LIBEV_FLAGS")) //EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */ flags = atoi (getenv ("LIBEV_FLAGS")); //Involves three functions ev_rt_now = ev_time (); // #define ev_rt_now((loop) ->ev_rt_now) to get the current calendar time mn_now = get_clock (); //#define mn_now((loop) ->mn_now) to get the current system time now_floor = mn_now; //#define now_floor ((loop)->now_floor) rtmn_diff = ev_rt_now - mn_now; //#define rtmn_diff ((loop)->rtmn_diff) #if EV_FEATURE_API //Feature Interface //#define EV_FEATURE_API ((EV_FEATURES) & 8) invoke_cb = ev_invoke_pending; //#define invoke_cb ((loop) ->invoke_cb) function pointer //ev_invoke_pending is a function #endif io_blocktime = 0.; timeout_blocktime = 0.; backend = 0; backend_fd = -1; sig_pending = 0; #if EV_ASYNC_ENABLE async_pending = 0; #endif pipe_write_skipped = 0; pipe_write_wanted = 0; evpipe [0] = -1; evpipe [1] = -1; //All of the above are initializations of loop members #if EV_USE_INOTIFY //Using Linux's inotify mechanism fs_fd = flags & EVFLAG_NOINOTIFY ? -1 : -2; #endif #if EV_USE_SIGNALFD //Using Linux signalfd mechanism sigfd = flags & EVFLAG_SIGNALFD ? -2 : -1; #endif if (!(flags & EVBACKEND_MASK)) flags |= ev_recommended_backends (); //flags = flags|ev_recommended_backends () //backend types supported by the current system, such as select, poll... choose how to initialize based on the underlying mechanism #if EV_USE_IOCP if (!backend && (flags & EVBACKEND_IOCP )) backend = iocp_init (EV_A_ flags); #endif #if EV_USE_PORT if (!backend && (flags & EVBACKEND_PORT )) backend = port_init (EV_A_ flags); #endif #if EV_USE_KQUEUE if (!backend && (flags & EVBACKEND_KQUEUE)) backend = kqueue_init (EV_A_ flags); #endif #if EV_USE_EPOLL if (!backend && (flags & EVBACKEND_EPOLL )) backend = epoll_init (EV_A_ flags); #endif #if EV_USE_POLL if (!backend && (flags & EVBACKEND_POLL )) backend = poll_init (EV_A_ flags); #endif #if EV_USE_SELECT if (!backend && (flags & EVBACKEND_SELECT)) backend = select_init (EV_A_ flags); #endif //These are the initial choices for underlying IO //Understand that this is a ready initialization //Initialize ev_prepare Monitor pending_w ev_prepare_init (&pending_w, pendingcb); //#define pending_w ((loop)->pending_w) //The second parameter is the function pointer, pendingcb is a function, but it is an empty function, there is no statement in the body of the function, the return value is void, the author explained: /* dummy callback for pending events */ //#define ev_prepare_init(ev,cb) do { ev_init ((ev), (cb)); ev_prepare_set ((ev)); } while (0) //Similar to ev_io_init // Equals ev_init() and ev_xxx_set() //However, there are many events without the ev_xxx_set() function, but to keep the code simple and canonical, uniform.. //The author uses a macro to define all ev_xxx_set() functions, which are empty if they do not exist. //Then add the following comment: /* nop, yes, this is a serious in-joke */ //#define ev_prepare_set(ev) /* nop, yes, this is a serious in-joke */ //If signal ing or async is supported #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE ev_init (&pipe_w, pipecb);//Initialize Pipeline Monitor pipe_w ev_set_priority (&pipe_w, EV_MAXPRI);//Set Priority Function //# define ev_set_priority(ev,pri) ( (ev_watcher *)(void *)(ev))->priority = (pri) #endif } }