https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69879

--- Comment #10 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Daniel Gutson from comment #0)
> This issue comes from the following discussion:
> 
> https://gcc.gnu.org/ml/libstdc++/2015-11/msg00009.html
> 
> In short: Pedro Alves suggested as a solution the addition of a pointer to
> the original (default) operator new and another for the operator delete,
> e.g. __default_operator_new and __default_operator_delete.
> This will allow users to override the global operators new and delete, and
> yet call the original implementations, for example to alter the behavior
> when there is no memory and the nothrow new should not iterate calling the
> new_handler.

Re-reading that thread, I'm not really convinced by the motivation. the
requirement is based on a fairly convoluted set of self-imposed conditions. 

Since PR 68210 was fixed, the no-throw versions of operator new are now coupled
to the throwing operator new. That means you only need to replace one version
of new/delete and the others will use it. So we probably don't need to expose a
__default_operator_xxx pointer for every overload now.

Also, the original problem described in
https://gcc.gnu.org/ml/libstdc++/2015-11/msg00009.html should no longer exist.
The new-handler should simply throw when it can't make more memory available
(as required by the standard), and that will cause the nothrow operator new to
return null.

There is a tangential problem though, which I am interested in.

ince PR 68210 was fixed, when libstdc++ itself is built with -fno-exceptions
then failure to allocate will abort the process, even if you use the nothrow
operator new.

I'm interested in a solution that allows operator new(nothrow_t, size_t) to
return null when built with -fno-exceptions.

One approach might be for operator new(nothrow_t, size_t) to detect when
operator new(size_t) has been replaced (which might involve comparing its
address with something like __default_operator_new_throw, and if the addresses
are the same (i.e. the user hasn't replaced operator new) then do the work
directly instead of calling operator new(size_t), but return null instead of
throwing.

That would make new(nothrow) useful again in environment where even libstdc++
is built with -fno-exceptions.

Also, the alternative implementation of operator new(nothrow_t, size_t) could
potentially use a different type of new-handler, which could be used to get the
behaviour that motivated this bug report and the thread above. 

So something like this in libsupc++/new_opnt.cc

using __nothrow_new_handler = bool(*)();
extern __nothrow_new_handler __nt_new_handler;

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz, const std::nothrow_t&) noexcept
{
#if __cpp_exceptions
  // _GLIBCXX_RESOLVE_LIB_DEFECTS
  // 206. operator new(size_t, nothrow) may become unlinked to ordinary
  // operator new if ordinary version replaced
  __try
    {
      return ::operator new(sz);
    }
  __catch (...)
    {
      return nullptr;
    }
#else // no-exceptions
  if ( /* operator new has been replaced */ )
    return ::operator new(sz);

  // otherwise, do the allocation directly so we can return null on failure

  void *p;

  /* malloc (0) is unpredictable; avoid it.  */
  if (__builtin_expect (sz == 0, false))
    sz = 1;

  while ((p = malloc (sz)) == 0)
    {
      if (__nt_new_handler && __nt_new_handler())
        continue; // retry
      new_handler handler = std::get_new_handler ();
      if (! handler)
        return 0;
      handler ();
    }

  return p;
#endif
}

This would allow a program to set a non-standard handler that doesn't have to
throw or terminate if it can't make memory available, causing the nothrow-new
to return null, which was the original aim.

I think something like this would solve the original problem, but *also* make
nothrow-new more useful for people who aren't installing new-handlers.

Reply via email to