Blame include/allocate_once.h

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