C-libev Learning Notes-Event Library Source Read 6-API-ev_default_loop(),ev_init()

Keywords: C++



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:


/* 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;


EV_API_DECL int ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; /* returns true when successful */



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.



# if defined __OPTIMIZE_SIZE__
#  define EV_FEATURES 0x7c
# else
#  define EV_FEATURES 0x7f
# 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
struct ev_loop * //Support function return value is ev_loop*
int //Return value int is not supported
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
      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 
      ev_default_loop_ptr = 1;

      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 */
        ev_default_loop_ptr = 0;

  return ev_default_loop_ptr;//Return structure pointer

Structures and structure pointers are defined as follows:

 //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 */


  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;

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()


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
//# define EV_USE_REALTIME 0
      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

#if EV_USE_MONOTONIC //System Startup Time
      if (!have_monotonic)
          struct timespec ts;

          if (!clock_gettime (CLOCK_MONOTONIC, &ts))
            have_monotonic = 1;

      /* 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

      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)
//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

      io_blocktime       = 0.;
      timeout_blocktime  = 0.;
      backend            = 0;
      backend_fd         = -1;
      sig_pending        = 0;
      async_pending      = 0;
      pipe_write_skipped = 0;
      pipe_write_wanted  = 0;
      evpipe [0]         = -1;
      evpipe [1]         = -1;
//All of the above are initializations of loop members

//Using Linux's inotify mechanism
      fs_fd              = flags & EVFLAG_NOINOTIFY ? -1 : -2;
//Using Linux signalfd mechanism
      sigfd              = flags & EVFLAG_SIGNALFD  ? -2 : -1;

      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 (!backend && (flags & EVBACKEND_IOCP  )) backend = iocp_init   (EV_A_ flags);
      if (!backend && (flags & EVBACKEND_PORT  )) backend = port_init   (EV_A_ flags);
      if (!backend && (flags & EVBACKEND_KQUEUE)) backend = kqueue_init (EV_A_ flags);
      if (!backend && (flags & EVBACKEND_EPOLL )) backend = epoll_init  (EV_A_ flags);
      if (!backend && (flags & EVBACKEND_POLL  )) backend = poll_init   (EV_A_ flags);
      if (!backend && (flags & EVBACKEND_SELECT)) backend = select_init (EV_A_ flags);
//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
      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)

Posted by seoreferrals on Mon, 20 Sep 2021 02:50:55 -0700