Blame include/allocate_once.h

Packit Service 82fcde
/* Allocate and initialize an object once, in a thread-safe fashion.
Packit Service 82fcde
   Copyright (C) 2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#ifndef _ALLOCATE_ONCE_H
Packit Service 82fcde
#define _ALLOCATE_ONCE_H
Packit Service 82fcde
Packit Service 82fcde
#include <atomic.h>
Packit Service 82fcde
Packit Service 82fcde
/* Slow path for allocate_once; see below.  */
Packit Service 82fcde
void *__libc_allocate_once_slow (void **__place,
Packit Service 82fcde
                                 void *(*__allocate) (void *__closure),
Packit Service 82fcde
                                 void (*__deallocate) (void *__closure,
Packit Service 82fcde
                                                       void *__ptr),
Packit Service 82fcde
                                 void *__closure);
Packit Service 82fcde
Packit Service 82fcde
/* Return an a pointer to an allocated and initialized data structure.
Packit Service 82fcde
   If this function returns a non-NULL value, the caller can assume
Packit Service 82fcde
   that pointed-to data has been initialized according to the ALLOCATE
Packit Service 82fcde
   function.
Packit Service 82fcde
Packit Service 82fcde
   It is expected that callers define an inline helper function which
Packit Service 82fcde
   adds type safety, like this.
Packit Service 82fcde
Packit Service 82fcde
   struct foo { ... };
Packit Service 82fcde
   struct foo *global_foo;
Packit Service 82fcde
   static void *allocate_foo (void *closure);
Packit Service 82fcde
   static void *deallocate_foo (void *closure, void *ptr);
Packit Service 82fcde
Packit Service 82fcde
   static inline struct foo *
Packit Service 82fcde
   get_foo (void)
Packit Service 82fcde
   {
Packit Service 82fcde
     return allocate_once (&global_foo, allocate_foo, free_foo, NULL);
Packit Service 82fcde
   }
Packit Service 82fcde
Packit Service 82fcde
   (Note that the global_foo variable is initialized to zero.)
Packit Service 82fcde
   Usage of this helper function looks like this:
Packit Service 82fcde
Packit Service 82fcde
   struct foo *local_foo = get_foo ();
Packit Service 82fcde
   if (local_foo == NULL)
Packit Service 82fcde
      report_allocation_failure ();
Packit Service 82fcde
Packit Service 82fcde
   allocate_once first performs an acquire MO load on *PLACE.  If the
Packit Service 82fcde
   result is not null, it is returned.  Otherwise, ALLOCATE (CLOSURE)
Packit Service 82fcde
   is called, yielding a value RESULT.  If RESULT equals NULL,
Packit Service 82fcde
   allocate_once returns NULL, and does not modify *PLACE (but another
Packit Service 82fcde
   thread may concurrently perform an allocation which succeeds,
Packit Service 82fcde
   updating *PLACE).  If RESULT does not equal NULL, the function uses
Packit Service 82fcde
   a CAS with acquire-release MO to update the NULL value in *PLACE
Packit Service 82fcde
   with the RESULT value.  If it turns out that *PLACE was updated
Packit Service 82fcde
   concurrently, allocate_once calls DEALLOCATE (CLOSURE, RESULT) to
Packit Service 82fcde
   undo the effect of ALLOCATE, and returns the new value of *PLACE
Packit Service 82fcde
   (after an acquire MO load).  If DEALLOCATE is NULL, free (RESULT)
Packit Service 82fcde
   is called instead.
Packit Service 82fcde
Packit Service 82fcde
   Compared to __libc_once, allocate_once has the advantage that it
Packit Service 82fcde
   does not need separate space for a control variable, and that it is
Packit Service 82fcde
   safe with regards to cancellation and other forms of exception
Packit Service 82fcde
   handling if the supplied callback functions are safe in that
Packit Service 82fcde
   regard.  allocate_once passes a closure parameter to the allocation
Packit Service 82fcde
   function, too.  */
Packit Service 82fcde
static inline void *
Packit Service 82fcde
allocate_once (void **__place, void *(*__allocate) (void *__closure),
Packit Service 82fcde
               void (*__deallocate) (void *__closure, void *__ptr),
Packit Service 82fcde
               void *__closure)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Synchronizes with the release MO CAS in
Packit Service 82fcde
     __allocate_once_slow.  */
Packit Service 82fcde
  void *__result = atomic_load_acquire (__place);
Packit Service 82fcde
  if (__result != NULL)
Packit Service 82fcde
    return __result;
Packit Service 82fcde
  else
Packit Service 82fcde
    return __libc_allocate_once_slow (__place, __allocate, __deallocate,
Packit Service 82fcde
                                      __closure);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#ifndef _ISOMAC
Packit Service 82fcde
libc_hidden_proto (__libc_allocate_once_slow)
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
#endif /* _ALLOCATE_ONCE_H */