master
  1/*
  2   Copyright (c) 2011-2016  mingw-w64 project
  3
  4   Permission is hereby granted, free of charge, to any person obtaining a
  5   copy of this software and associated documentation files (the "Software"),
  6   to deal in the Software without restriction, including without limitation
  7   the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8   and/or sell copies of the Software, and to permit persons to whom the
  9   Software is furnished to do so, subject to the following conditions:
 10
 11   The above copyright notice and this permission notice shall be included in
 12   all copies or substantial portions of the Software.
 13
 14   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 17   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 19   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 20   DEALINGS IN THE SOFTWARE.
 21*/
 22
 23#ifdef HAVE_CONFIG_H
 24#include "config.h"
 25#endif
 26
 27#include <assert.h>
 28#include <malloc.h>
 29#include <stdio.h>
 30
 31#define WIN32_LEAN_AND_MEAN
 32#include <windows.h>
 33
 34/* public header files */
 35#include "pthread.h"
 36#include "semaphore.h"
 37/* internal header files */
 38#include "barrier.h"
 39#include "misc.h"
 40
 41static pthread_spinlock_t barrier_global = PTHREAD_SPINLOCK_INITIALIZER;
 42
 43static WINPTHREADS_ATTRIBUTE((noinline)) int
 44barrier_unref(volatile pthread_barrier_t *barrier, int res)
 45{
 46    pthread_spin_lock(&barrier_global);
 47    assert((((barrier_t *)*barrier)->valid == LIFE_BARRIER) && (((barrier_t *)*barrier)->busy > 0));
 48     ((barrier_t *)*barrier)->busy -= 1;
 49    pthread_spin_unlock(&barrier_global);
 50    return res;
 51}
 52
 53static WINPTHREADS_ATTRIBUTE((noinline)) int barrier_ref(volatile pthread_barrier_t *barrier)
 54{
 55    int r = 0;
 56    pthread_spin_lock(&barrier_global);
 57
 58    if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL;
 59    else {
 60        ((barrier_t *)*barrier)->busy += 1;
 61    }
 62
 63    pthread_spin_unlock(&barrier_global);
 64
 65    return r;
 66}
 67
 68static WINPTHREADS_ATTRIBUTE((noinline))  int
 69barrier_ref_destroy(volatile pthread_barrier_t *barrier, pthread_barrier_t *bDestroy)
 70{
 71    int r = 0;
 72
 73    *bDestroy = NULL;
 74    pthread_spin_lock(&barrier_global);
 75
 76    if (!barrier || !*barrier || ((barrier_t *)*barrier)->valid != LIFE_BARRIER) r = EINVAL;
 77    else {
 78        barrier_t *b_ = (barrier_t *)*barrier;
 79        if (b_->busy) r = EBUSY;
 80        else {
 81            *bDestroy = *barrier;
 82            *barrier = NULL;
 83        }
 84    }
 85
 86    pthread_spin_unlock(&barrier_global);
 87    return r;
 88}
 89
 90static WINPTHREADS_ATTRIBUTE((noinline)) void
 91barrier_ref_set (volatile pthread_barrier_t *barrier, void *v)
 92{
 93  pthread_spin_lock(&barrier_global);
 94  *barrier = v;
 95  pthread_spin_unlock(&barrier_global);
 96}
 97
 98int pthread_barrier_destroy(pthread_barrier_t *b_)
 99{
100    pthread_barrier_t bDestroy;
101    barrier_t *b;
102    int r;
103
104    while ((r = barrier_ref_destroy(b_,&bDestroy)) == EBUSY)
105      Sleep(0);
106
107    if (r)
108      return r;
109
110    b = (barrier_t *)bDestroy;
111
112    pthread_mutex_lock(&b->m);
113
114    if (sem_destroy(&b->sems[0]) != 0)
115    {
116        /* Could this happen? */
117        *b_ = bDestroy;
118        pthread_mutex_unlock (&b->m);
119        return EBUSY;
120    }
121    if (sem_destroy(&b->sems[1]) != 0)
122    {
123      sem_init (&b->sems[0], b->share, 0);
124      *b_ = bDestroy;
125      pthread_mutex_unlock (&b->m);
126      return -1;
127    }
128    pthread_mutex_unlock(&b->m);
129    if(pthread_mutex_destroy(&b->m) != 0) {
130     sem_init (&b->sems[0], b->share, 0);
131     sem_init (&b->sems[1], b->share, 0);
132     *b_ = bDestroy;
133     return -1;
134    }
135    b->valid = DEAD_BARRIER;
136    free(bDestroy);
137    return 0;
138
139}
140
141int
142pthread_barrier_init (pthread_barrier_t *b_, const void *attr,
143		      unsigned int count)
144{
145    barrier_t *b;
146
147    if (!count || !b_)
148      return EINVAL;
149
150    if ((b = (pthread_barrier_t)calloc(1,sizeof(*b))) == NULL)
151       return ENOMEM;
152    if (!attr || *((int **)attr) == NULL)
153      b->share = PTHREAD_PROCESS_PRIVATE;
154    else
155      memcpy (&b->share, *((void **) attr), sizeof (int));
156    b->total = count;
157    b->count = count;
158    b->valid = LIFE_BARRIER;
159    b->sel = 0;
160
161    if (pthread_mutex_init(&b->m, NULL) != 0)
162    {
163      free (b);
164      return ENOMEM;
165    }
166
167    if (sem_init(&b->sems[0], b->share, 0) != 0)
168    {
169       pthread_mutex_destroy(&b->m);
170       free (b);
171       return ENOMEM;
172    }
173    if (sem_init(&b->sems[1], b->share, 0) != 0)
174    {
175       pthread_mutex_destroy(&b->m);
176       sem_destroy(&b->sems[0]);
177       free (b);
178       return ENOMEM;
179    }
180    barrier_ref_set (b_,b);
181
182    return 0;
183}
184
185int pthread_barrier_wait(pthread_barrier_t *b_)
186{
187  long sel;
188  int r, e, rslt;
189  barrier_t *b;
190
191  r = barrier_ref(b_);
192  if(r) return r;
193
194  b = (barrier_t *)*b_;
195
196  if ((r = pthread_mutex_lock(&b->m)) != 0) return  barrier_unref(b_,EINVAL);
197  sel = b->sel;
198  InterlockedDecrement((long*)&b->total);
199  if (b->total == 0)
200  {
201    b->total = b->count;
202    b->sel = (sel != 0 ? 0 : 1);
203    e = 1;
204    rslt = PTHREAD_BARRIER_SERIAL_THREAD;
205    r = (b->count > 1 ? sem_post_multiple (&b->sems[sel], b->count - 1) : 0);
206  }
207  else { e = 0; rslt= 0; }
208  pthread_mutex_unlock(&b->m);
209  if (!e)
210    r = sem_wait(&b->sems[sel]);
211
212  if (!r) r = rslt;
213  return barrier_unref(b_,r);
214}
215
216int pthread_barrierattr_init(void **attr)
217{
218  int *p;
219
220  if ((p = (int *) calloc (1, sizeof (int))) == NULL)
221    return ENOMEM;
222
223  *p = PTHREAD_PROCESS_PRIVATE;
224  *attr = p;
225
226  return 0;
227}
228
229int pthread_barrierattr_destroy(void **attr)
230{
231  void *p;
232  if (!attr || (p = *attr) == NULL)
233    return EINVAL;
234  *attr = NULL;
235  free (p);
236  return 0;
237}
238
239int pthread_barrierattr_setpshared(void **attr, int s)
240{
241  if (!attr || *attr == NULL
242      || (s != PTHREAD_PROCESS_SHARED && s != PTHREAD_PROCESS_PRIVATE))
243    return EINVAL;
244  memcpy (*attr, &s, sizeof (int));
245  return 0;
246}
247
248int pthread_barrierattr_getpshared(void **attr, int *s)
249{
250  if (!attr || !s || *attr == NULL)
251    return EINVAL;
252  memcpy (s, *attr, sizeof (int));
253  return 0;
254}