1 /* Copyright (C) 2001-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <errno.h>
19 #include <netdb.h>
20 #include <pthread.h>
21 #include <stdlib.h>
22 #include <sys/time.h>
23 
24 #include <gai_misc.h>
25 
26 int
___gai_suspend_time64(const struct gaicb * const list[],int ent,const struct __timespec64 * timeout)27 ___gai_suspend_time64 (const struct gaicb *const list[], int ent,
28 		       const struct __timespec64 *timeout)
29 {
30   struct waitlist waitlist[ent];
31   struct requestlist *requestlist[ent];
32 #ifndef DONT_NEED_GAI_MISC_COND
33   pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
34 #endif
35   int cnt;
36   unsigned int cntr = 1;
37   int none = 1;
38   int result;
39 
40   /* Request the mutex.  */
41   __pthread_mutex_lock (&__gai_requests_mutex);
42 
43   /* There is not yet a finished request.  Signal the request that
44      we are working for it.  */
45   for (cnt = 0; cnt < ent; ++cnt)
46     if (list[cnt] != NULL && list[cnt]->__return == EAI_INPROGRESS)
47       {
48 	requestlist[cnt] = __gai_find_request (list[cnt]);
49 
50 	if (requestlist[cnt] != NULL)
51 	  {
52 #ifndef DONT_NEED_GAI_MISC_COND
53 	    waitlist[cnt].cond = &cond;
54 #endif
55 	    waitlist[cnt].next = requestlist[cnt]->waiting;
56 	    waitlist[cnt].counterp = &cntr;
57 	    waitlist[cnt].sigevp = NULL;
58 	    waitlist[cnt].caller_pid = 0;	/* Not needed.  */
59 	    requestlist[cnt]->waiting = &waitlist[cnt];
60 	    none = 0;
61 	  }
62       }
63 
64   struct __timespec64 ts;
65   if (timeout != NULL)
66     {
67       __clock_gettime64 (CLOCK_MONOTONIC, &ts);
68       ts.tv_sec += timeout->tv_sec;
69       ts.tv_nsec += timeout->tv_nsec;
70       if (ts.tv_nsec >= 1000000000)
71 	{
72 	  ts.tv_nsec -= 1000000000;
73 	  ts.tv_sec++;
74 	}
75     }
76 
77   if (none)
78     {
79       if (cnt < ent)
80 	/* There is an entry which is finished.  */
81 	result = 0;
82       else
83 	result = EAI_ALLDONE;
84     }
85   else
86     {
87       /* There is no request done but some are still being worked on.  */
88       int oldstate;
89 
90       /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancelation
91 	 points we must be careful.  We added entries to the waiting lists
92 	 which we must remove.  So defer cancelation for now.  */
93       __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
94 
95 #ifdef DONT_NEED_GAI_MISC_COND
96       result = 0;
97       GAI_MISC_WAIT (result, cntr, timeout == NULL ? NULL : &ts, 1);
98 #else
99       struct timespec ts32 = valid_timespec64_to_timespec (ts);
100       result = pthread_cond_timedwait (&cond, &__gai_requests_mutex,
101                                        timeout == NULL ? NULL : &ts32);
102 #endif
103 
104       /* Now remove the entry in the waiting list for all requests
105 	 which didn't terminate.  */
106       for (cnt = 0; cnt < ent; ++cnt)
107 	if (list[cnt] != NULL && list[cnt]->__return == EAI_INPROGRESS
108 	    && requestlist[cnt] != NULL)
109 	  {
110 	    struct waitlist **listp = &requestlist[cnt]->waiting;
111 
112 	    /* There is the chance that we cannot find our entry anymore.
113 	       This could happen if the request terminated and restarted
114 	       again.  */
115 	    while (*listp != NULL && *listp != &waitlist[cnt])
116 	      listp = &(*listp)->next;
117 
118 	    if (*listp != NULL)
119 	      *listp = (*listp)->next;
120 	  }
121 
122       /* Now it's time to restore the cancelation state.  */
123       __pthread_setcancelstate (oldstate, NULL);
124 
125 #ifndef DONT_NEED_GAI_MISC_COND
126       /* Release the conditional variable.  */
127       if (pthread_cond_destroy (&cond) != 0)
128 	/* This must never happen.  */
129 	abort ();
130 #endif
131 
132       if (result != 0)
133 	{
134 	  /* An error occurred.  Possibly it's EINTR.  We have to translate
135 	     the timeout error report of `pthread_cond_timedwait' to the
136 	     form expected from `gai_suspend'.  */
137 	  if (__glibc_likely (result == ETIMEDOUT))
138 	    result = EAI_AGAIN;
139 	  else if (result == EINTR)
140 	    result = EAI_INTR;
141 	  else
142 	    result = EAI_SYSTEM;
143 	}
144     }
145 
146   /* Release the mutex.  */
147   __pthread_mutex_unlock (&__gai_requests_mutex);
148 
149   return result;
150 }
151 
152 #if __TIMESIZE == 64
153 # if PTHREAD_IN_LIBC
154 versioned_symbol (libc, ___gai_suspend_time64, gai_suspend, GLIBC_2_34);
155 #  if OTHER_SHLIB_COMPAT (libanl, GLIBC_2_2_3, GLIBC_2_34)
156 compat_symbol (libanl, ___gai_suspend_time64, gai_suspend, GLIBC_2_2_3);
157 #  endif
158 # endif /* PTHREAD_IN_LIBC */
159 
160 #else /* __TIMESIZE != 64 */
161 # if PTHREAD_IN_LIBC
162 libc_hidden_ver (___gai_suspend_time64, __gai_suspend_time64)
163 versioned_symbol (libc, ___gai_suspend_time64, __gai_suspend_time64,
164 		  GLIBC_2_34);
165 # else /* !PTHREAD_IN_LIBC */
166 # if IS_IN (libanl)
hidden_ver(___gai_suspend_time64,__gai_suspend_time64)167 hidden_ver (___gai_suspend_time64, __gai_suspend_time64)
168 # endif
169 #endif /* !PTHREAD_IN_LIBC */
170 
171 int
172 ___gai_suspend (const struct gaicb *const list[], int ent,
173 		const struct timespec *timeout)
174 {
175   struct __timespec64 ts64;
176 
177   if (timeout != NULL)
178     ts64 = valid_timespec_to_timespec64 (*timeout);
179 
180   return __gai_suspend_time64 (list, ent, timeout != NULL ? &ts64 : NULL);
181 }
182 #if PTHREAD_IN_LIBC
183 versioned_symbol (libc, ___gai_suspend, gai_suspend, GLIBC_2_34);
184 # if OTHER_SHLIB_COMPAT (libanl, GLIBC_2_2_3, GLIBC_2_34)
185 compat_symbol (libanl, ___gai_suspend, gai_suspend, GLIBC_2_2_3);
186 # endif
187 # else
188 weak_alias (___gai_suspend, gai_suspend)
189 # endif /* !PTHREAD_IN_LIBC */
190 #endif /* __TIMESIZE != 64 */
191