Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief File Descriptor based Poll Abstraction Layer (FAL)
0003  *
0004  * @file sys_fdpoll.c
0005  */
0006 /* -----------------------------------------------------------------------------
0007  * Enduro/X Middleware Platform for Distributed Transaction Processing
0008  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0009  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0010  * This software is released under one of the following licenses:
0011  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0012  * See LICENSE file for full text.
0013  * -----------------------------------------------------------------------------
0014  * AGPL license:
0015  *
0016  * This program is free software; you can redistribute it and/or modify it under
0017  * the terms of the GNU Affero General Public License, version 3 as published
0018  * by the Free Software Foundation;
0019  *
0020  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0021  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0022  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0023  * for more details.
0024  *
0025  * You should have received a copy of the GNU Affero General Public License along 
0026  * with this program; if not, write to the Free Software Foundation, Inc.,
0027  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0028  *
0029  * -----------------------------------------------------------------------------
0030  * A commercial use license is available from Mavimax, Ltd
0031  * contact@mavimax.com
0032  * -----------------------------------------------------------------------------
0033  */
0034 
0035 /*---------------------------Includes-----------------------------------*/
0036 #include <stdio.h>
0037 #include <stdlib.h>
0038 #include <time.h>
0039 
0040 #include <unistd.h>
0041 #include <stdarg.h>
0042 #include <ctype.h>
0043 #include <memory.h>
0044 #include <errno.h>
0045 #include <signal.h>
0046 #include <limits.h>
0047 #include <pthread.h>
0048 #include <string.h>
0049 
0050 #include <ndrstandard.h>
0051 #include <ndebug.h>
0052 #include <nstdutil.h>
0053 #include <limits.h>
0054 
0055 #include <sys_unix.h>
0056 
0057 
0058 /*---------------------------Externs------------------------------------*/
0059 /*---------------------------Macros-------------------------------------*/
0060 #define EX_POLL_SETS_MAX            1024
0061 
0062 #define ERROR_BUFFER            1024
0063 
0064 #define EX_EPOLL_API_ENTRY      {NSTD_TLS_ENTRY; \
0065             G_nstd_tls->M_last_err = 0; \
0066             G_nstd_tls->M_last_err_msg[0] = EXEOS;}
0067 /*---------------------------Enums--------------------------------------*/
0068 /*---------------------------Typedefs-----------------------------------*/
0069 
0070 /*
0071  * Our internal 'epoll' set
0072  */
0073 struct ndrx_epoll_set
0074 {
0075     int fd;
0076     
0077     struct pollfd *fdtab;   /* set off poll FDs */
0078     int nrfds; /* number of FDs in poll */
0079     
0080     EX_hash_handle hh;         /* makes this structure hashable */
0081 };
0082 typedef struct ndrx_epoll_set ndrx_epoll_set_t;
0083 
0084 /*---------------------------Globals------------------------------------*/
0085 
0086 exprivate ndrx_epoll_set_t *M_psets = NULL; /* poll sets  */
0087 MUTEX_LOCKDECL(M_psets_lock);
0088 
0089 /*---------------------------Statics------------------------------------*/
0090 /*---------------------------Prototypes---------------------------------*/
0091 
0092 
0093 /**
0094  * Find polling set (must be locked externally...)
0095  * @param epfd poll set to lookup
0096  * @return Poll set struct
0097  */
0098 exprivate ndrx_epoll_set_t* pset_find(int epfd)
0099 {
0100     ndrx_epoll_set_t *ret = NULL;
0101     
0102     EXHASH_FIND_INT( M_psets, &epfd, ret);
0103     
0104     return ret;
0105 }
0106 
0107 /**
0108  * Find FD in hash
0109  * @param pset  POLL Set
0110  * @param fd    File Descriptor to find
0111  * @return FD or FAIL
0112  */
0113 exprivate int fd_find(ndrx_epoll_set_t *pset, int fd)
0114 {
0115     int i;
0116     
0117     for (i=0; i<pset->nrfds; i++)
0118     {
0119         if (fd==pset->fdtab[i].fd)
0120         {
0121             return i;
0122         }
0123     }
0124     
0125     return EXFAIL;
0126 }
0127 
0128 
0129 /**
0130  * We basically will use unix error codes
0131  */
0132 exprivate void ndrx_epoll_set_err(int error_code, const char *fmt, ...)
0133 {
0134     char msg[ERROR_BUFFER+1] = {EXEOS};
0135     va_list ap;
0136     
0137     NSTD_TLS_ENTRY;
0138 
0139     va_start(ap, fmt);
0140     (void) vsnprintf(msg, sizeof(msg), fmt, ap);
0141     va_end(ap);
0142 
0143     strcpy(G_nstd_tls->M_last_err_msg, msg);
0144     G_nstd_tls->M_last_err = error_code;
0145 
0146     NDRX_LOG(log_warn, "ndrx_epoll_set_err: %d (%s) (%s)",
0147                     error_code, strerror(G_nstd_tls->M_last_err), 
0148                     G_nstd_tls->M_last_err_msg);
0149     
0150 }
0151 
0152 /**
0153  * Nothing to init for epoll()
0154  */
0155 expublic inline void ndrx_epoll_sys_init(void)
0156 {
0157     return;
0158 }
0159 
0160 /**
0161  * Nothing to un-init for epoll()
0162  */
0163 expublic inline void ndrx_epoll_sys_uninit(void)
0164 {
0165     return;
0166 }
0167 
0168 /**
0169  * Return the compiled poll mode
0170  * @return 
0171  */
0172 expublic inline char * ndrx_epoll_mode(void)
0173 {
0174     static char *mode = "fdpoll";
0175     
0176     return mode;
0177 }
0178 /**
0179  * Wrapper for epoll_ctl, for standard file descriptors
0180  * @param epfd
0181  * @param op
0182  * @param fd
0183  * @param event
0184  * @return 
0185  */
0186 expublic inline int ndrx_epoll_ctl(int epfd, int op, int fd, struct ndrx_epoll_event *event)
0187 {   
0188     int ret = EXSUCCEED;
0189     ndrx_epoll_set_t* set = NULL;
0190     char *fn = "ndrx_epoll_ctl";
0191     
0192     EX_EPOLL_API_ENTRY;
0193     
0194     MUTEX_LOCK_V(M_psets_lock);
0195     
0196     if (NULL==(set = pset_find(epfd)))
0197     {
0198         NDRX_LOG(log_error, "ndrx_epoll set %d not found", epfd);
0199         ndrx_epoll_set_err(ENOSYS, "ndrx_epoll set %d not found", epfd);
0200         EXFAIL_OUT(ret);
0201     }
0202     
0203     if (EX_EPOLL_CTL_ADD == op)
0204     {
0205         NDRX_LOG(log_info, "%s: Add operation on ndrx_epoll set %d, fd %d", fn, epfd, fd);
0206         
0207         /* test & add to FD hash */
0208         if (EXFAIL!=fd_find(set, fd))
0209         {
0210 
0211             ndrx_epoll_set_err(EINVAL, "fd %d already exists in ndrx_epoll set (epfd %d)", 
0212                     fd, set->fd);
0213             NDRX_LOG(log_error, "fd %d already exists in ndrx_epoll set (epfd %d)", 
0214                  fd, set->fd);
0215             EXFAIL_OUT(ret);
0216         }
0217         
0218         /* resize/realloc events list, add fd */
0219         set->nrfds++;
0220         
0221         NDRX_LOG(log_info, "set nrfds incremented to %d", set->nrfds);
0222         
0223         if (NULL==(set->fdtab=NDRX_REALLOC(set->fdtab, sizeof(struct pollfd)*set->nrfds)))
0224         {
0225             ndrx_epoll_set_err(errno, "Failed to realloc %d/%d", 
0226                     set->nrfds, sizeof(struct pollfd)*set->nrfds);
0227             NDRX_LOG(log_error, "Failed to realloc %d/%d", 
0228                     set->nrfds, sizeof(struct pollfd)*set->nrfds);
0229             EXFAIL_OUT(ret);
0230         }
0231         
0232         set->fdtab[set->nrfds-1].fd = fd;
0233         set->fdtab[set->nrfds-1].events = event->events;
0234         
0235     }
0236     else if (EX_EPOLL_CTL_DEL == op)
0237     {
0238         int found = EXFALSE;
0239         int i;
0240         NDRX_LOG(log_info, "%s: Delete operation on ndrx_epoll set %d, fd %d", fn, epfd, fd);
0241         
0242         /* test & add to FD hash */
0243         if (EXFAIL==fd_find(set, fd))
0244         {
0245             ndrx_epoll_set_err(EINVAL, "fd %d not found in ndrx_epoll set (epfd %d)", 
0246                     fd, set->fd);
0247             NDRX_LOG(log_error, "fd %d not found in ndrx_epoll set (epfd %d)", 
0248                     fd, set->fd);
0249             EXFAIL_OUT(ret);
0250         }
0251         
0252         /* Remove fd from set->fdtab & from hash */
0253         
0254         for (i = 0; i < set->nrfds; i++)
0255         {
0256             if (set->fdtab[i].fd == fd)
0257             {
0258                 /* kill the element */
0259                 if (i!=set->nrfds-1 && set->nrfds>1)
0260                 {
0261                     /* It is not last element, thus we can move something... */
0262                     memmove(&set->fdtab[i], &set->fdtab[i+1], 
0263                             sizeof(struct pollfd)*(set->nrfds-i-1));
0264                 }
0265                 
0266                 set->nrfds--;
0267                 
0268                 NDRX_LOG(log_info, "set nrfds decremented to %d", set->nrfds);
0269                 
0270                 if (0==set->nrfds)
0271                 {
0272                     NDRX_LOG(log_warn, "set->nrfds == 0, => free");
0273                     NDRX_FREE((char *)set->fdtab);
0274                 }
0275                 else if (NULL==(set->fdtab=NDRX_REALLOC(set->fdtab, 
0276                         sizeof(struct pollfd)*set->nrfds)))
0277                 {
0278                     ndrx_epoll_set_err(errno, "Failed to realloc %d/%d", 
0279                             set->nrfds, sizeof(struct pollfd)*set->nrfds);
0280                     
0281                     NDRX_LOG(log_error, "Failed to realloc %d/%d", 
0282                             set->nrfds, sizeof(struct pollfd)*set->nrfds);
0283                     
0284                     EXFAIL_OUT(ret);
0285                 }
0286                 
0287                 found = EXTRUE;
0288                 
0289                 break;
0290             }
0291         }
0292         
0293         if (!found)
0294         {
0295             NDRX_LOG(log_warn, "File descriptor %d was requested for removal, "
0296                     "but not found epoll set %d!", fd, epfd);
0297         }
0298     }
0299     else
0300     {
0301         ndrx_epoll_set_err(EINVAL, "Invalid operation %d", op);
0302         NDRX_LOG(log_error, "Invalid operation %d", op);
0303         
0304         EXFAIL_OUT(ret);
0305     }
0306 
0307 out:
0308 
0309     MUTEX_UNLOCK_V(M_psets_lock);
0310 
0311     NDRX_LOG(log_info, "%s return %d", fn, ret);
0312 
0313     return ret;
0314 }
0315 
0316 /**
0317  * epoll_ctl for Posix queue descriptors
0318  * @param epfd
0319  * @param op
0320  * @param fd
0321  * @param event
0322  * @return 
0323  */
0324 expublic inline int ndrx_epoll_ctl_mq(int epfd, int op, mqd_t fd, struct ndrx_epoll_event *event)
0325 {
0326     return ndrx_epoll_ctl(epfd, op, (int)fd, event);
0327 }
0328 
0329 /**
0330  * Wrapper for epoll_create
0331  * @param size
0332  * @return 
0333  */
0334 expublic inline int ndrx_epoll_create(int size)
0335 {
0336     int ret = EXSUCCEED;
0337     int i = 1;
0338     ndrx_epoll_set_t *set;
0339     
0340     EX_EPOLL_API_ENTRY;
0341     
0342     while (NULL!=(set=pset_find(i)) && i < EX_POLL_SETS_MAX)
0343     {
0344         i++;
0345     }
0346     
0347     /* we must have free set */
0348     if (NULL!=set)
0349     {
0350         ndrx_epoll_set_err(EMFILE, "Max ndrx_epoll_sets_reached");
0351         NDRX_LOG(log_error, "Max ndrx_epoll_sets_reached");
0352                 
0353         
0354         set = NULL;
0355         EXFAIL_OUT(ret);
0356     }
0357     
0358     NDRX_LOG(log_info, "Creating ndrx_epoll set: %d", i);
0359     
0360     if (NULL==(set = (ndrx_epoll_set_t *)NDRX_CALLOC(1, sizeof(*set))))
0361     {
0362         ndrx_epoll_set_err(errno, "Failed to alloc: %d bytes", sizeof(*set));
0363         
0364         NDRX_LOG(log_error, "Failed to alloc: %d bytes", sizeof(*set));
0365 
0366         EXFAIL_OUT(ret);
0367     }
0368     
0369     set->nrfds = 0;
0370     set->fd = i; /* assign the FD num */
0371     
0372     /* add finally to hash */
0373     MUTEX_LOCK_V(M_psets_lock);
0374     EXHASH_ADD_INT(M_psets, fd, set);
0375     MUTEX_UNLOCK_V(M_psets_lock);
0376     
0377     NDRX_LOG(log_info, "ndrx_epoll_create succeed, fd=%d", i);
0378     
0379 out:
0380 
0381     if (EXSUCCEED!=ret)
0382     {
0383             
0384         if (NULL!=set)
0385         {
0386             NDRX_FREE((char *)set);
0387         }
0388         
0389         return EXFAIL;        
0390     }
0391 
0392     return i;
0393 }
0394 
0395 /**
0396  * Close Epoll set.
0397  */
0398 expublic inline int ndrx_epoll_close(int epfd)
0399 {
0400     int ret = EXSUCCEED;
0401     ndrx_epoll_set_t* set = NULL;
0402     
0403     NDRX_LOG(log_debug, "ndrx_epoll_close(%d) enter", epfd);
0404             
0405     MUTEX_LOCK_V(M_psets_lock);
0406     
0407     NDRX_LOG(log_debug, "ndrx_epoll_close(%d) enter (after lock", epfd);
0408     
0409     if (NULL==(set = pset_find(epfd)))
0410     {
0411         MUTEX_UNLOCK_V(M_psets_lock); /*  <<< release the lock */
0412         
0413         ndrx_epoll_set_err(EINVAL, "ndrx_epoll_close set %d not found", epfd);
0414         NDRX_LOG(log_error, "ndrx_epoll_close set %d not found", epfd);
0415         
0416         
0417         EXFAIL_OUT(ret);
0418     }
0419     MUTEX_UNLOCK_V(M_psets_lock);
0420     
0421     while (set->nrfds > 0)
0422     {
0423         ndrx_epoll_ctl(set->fd, EX_EPOLL_CTL_DEL, set->fdtab[0].fd, NULL);
0424     }
0425     
0426     MUTEX_LOCK_V(M_psets_lock);
0427     EXHASH_DEL(M_psets, set);
0428     NDRX_FREE(set);
0429     MUTEX_UNLOCK_V(M_psets_lock);
0430     
0431 out:
0432     return EXFAIL;
0433 }
0434 
0435 /**
0436  * Wrapper for epoll_wait
0437  * @param epfd
0438  * @param events
0439  * @param maxevents
0440  * @param timeout
0441  * @param buf read pre-loaded msg (not used here)
0442  * @param buf_len  pre-load buffer len
0443  * @return 
0444  */
0445 expublic inline int ndrx_epoll_wait(int epfd, struct ndrx_epoll_event *events, int maxevents, int timeout,
0446             char **buf, int *buf_len)
0447 {
0448     int ret = EXSUCCEED;
0449     int numevents = 0;
0450     ndrx_epoll_set_t* set = NULL;
0451     char *fn = "ndrx_epoll_wait";
0452     int i;
0453     int retpoll;
0454     
0455     EX_EPOLL_API_ENTRY;
0456     
0457     /*  !!!! LOCKED AREA !!!! */
0458     MUTEX_LOCK_V(M_psets_lock);
0459     
0460     if (NULL==(set = pset_find(epfd)))
0461     {
0462         MUTEX_UNLOCK_V(M_psets_lock); /*  <<< release the lock */
0463         
0464         ndrx_epoll_set_err(EINVAL, "ndrx_epoll set %d not found", epfd);
0465         
0466         NDRX_LOG(log_error, "ndrx_epoll set %d not found", epfd);
0467         
0468         EXFAIL_OUT(ret);
0469     }
0470     
0471     MUTEX_UNLOCK_V(M_psets_lock); /*  <<< release the lock */
0472     
0473     /*  !!!! LOCKED AREA, end !!!! */
0474     
0475     /* Reset received events... */
0476    
0477     
0478     for (i=0; i<set->nrfds; i++)
0479     {
0480         set->fdtab[i].revents = 0;
0481         NDRX_LOG(log_debug, "poll i=%d, fd=%d, events=%d", 
0482                 i, set->fdtab[i].fd, set->fdtab[i].events);
0483     }
0484 
0485     NDRX_LOG(log_debug, "%s: epfd=%d, events=%p, maxevents=%d, "
0486                     "timeout=%d - about to poll(nrfds=%d)",
0487                     fn, epfd, events, maxevents, timeout, set->nrfds);
0488         
0489     retpoll = poll( set->fdtab, set->nrfds, timeout);
0490     
0491     NDRX_LOG(log_debug, "retpoll=%d", retpoll);
0492     
0493     for (i=0; i<set->nrfds; i++)
0494     {
0495         NDRX_LOG(log_debug, "fd=%d, i=%d revents=%d", 
0496                 set->fdtab[i].fd, i, set->fdtab[i].revents);
0497         if (set->fdtab[i].revents && maxevents > numevents)
0498         {
0499             NDRX_LOG(log_debug, "Returning...");
0500 
0501             numevents++;
0502             
0503             events[numevents-1].data.fd = set->fdtab[i].fd;
0504             events[numevents-1].events = set->fdtab[i].revents;
0505             events[numevents-1].is_mqd = EXFAIL;
0506     }
0507     }
0508     
0509 out:
0510 
0511     NDRX_LOG(log_info, "%s ret=%d numevents=%d", fn, ret, numevents);
0512 
0513     if (EXSUCCEED==ret)
0514     {
0515         return numevents;
0516     }
0517     else
0518     {
0519         return EXFAIL;
0520     }
0521 }
0522 
0523 /**
0524  * Return errno for ndrx_poll() operation
0525  * @return 
0526  */
0527 expublic int ndrx_epoll_errno(void)
0528 {
0529     NSTD_TLS_ENTRY;
0530     return G_nstd_tls->M_last_err;
0531 }
0532 
0533 /**
0534  * Wrapper for strerror
0535  * @param err
0536  * @return 
0537  */
0538 expublic char * ndrx_poll_strerror(int err)
0539 {
0540     NSTD_TLS_ENTRY;
0541     
0542     sprintf(G_nstd_tls->poll_strerr, "%s (last error: %s)", 
0543     strerror(err), G_nstd_tls->M_last_err_msg);
0544     
0545     return G_nstd_tls->poll_strerr;
0546 }
0547 
0548 /* vim: set ts=4 sw=4 et smartindent: */