Hi,

On 02/20/2013 06:16 PM, Marc Lehmann wrote:
On Wed, Feb 20, 2013 at 05:28:57PM +0100, Alexander Klauer 
<[email protected]> wrote:
semantics as a call to free(ptr). A C89-conformant implementation may
return a unique non-NULL pointer which may be safely passed to
free().
And when does this happen *in libev*? realloc frees the memory for cases
where the ptr is != 0 and size = 0, which is all that libev relies on. And
currently, the default allocator calls free in all cases where size == 0
for portability.


This is correct but it's not enough. The problem is more subtle, and my first few posts may have added to the confusion. Sorry about that. Let me collect and order the various things which came up during the discussion.

1. The malloc(3) man page by the Linux man pages project, version 3.35, dated 2011-09-08 does indeed say about realloc(): "If
the new size is larger than the old size, the added memory will not be
initialized. If ptr is NULL, then the call is equivalent to mal‐
loc(size), for all values of size; if size is equal to zero, and ptr is
not NULL, then the call is equivalent to free(ptr)."
The last assertion is incorrect. See 4. for a proof on concept in libev below.

2. The C89 standard says "If the size of the space requested is zero, the behavior is implementation-defined; the value returned shall be either a null pointer or a unique pointer." The realloc() shipped with glibc 2.15 is C89-compliant, taking the second choice. In particular, a realloc(ptr, 0) does all that which free(ptr) does, but it does something on top: every such call returns a unique non-null pointer, which may later be safely passed to free(). So thecalls are not equivalent in the strict sense. Yes, the space pointed to by ptr *is* effectively freed. However, the C library must keep some internalinformation as to which pointers may be passed to free(), and it's that information which takes upmemory.

3. The libev default allocator in the current CVS head does the right thing bycalling free() when the passed size is zero. However, the libev documentation says that the allocator merely has to have "semantics [...] identical to the |realloc| C89/SuS/POSIX function" (minor nit: C89 realloc()'s second parameter has type size_t while the libev allocator expects a long), which is not enough.

4. I have attached a proof of concept demonstrating the problem. The attached program effectively replaces the libev standard allocator with realloc().

a. Compile/link with gcc -Wall -Wextra -pedantic libev.c ../libev_cvs/.libs/libev.a -lm b. Set ulimit -v 262144 or something, lest the OOM killer rears its ugly head(on a desktop configuration).
c. Run ./a.out.

On my desktop system (Linux 3.2.0-35-generic #55-Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux glibc 2.15), the program runs for a few seconds, and then ends with

(libev) cannot allocate 768 bytes, aborting.Aborted (core dumped)


I hope this postclarifies the issue.

Best regards,
Alexander
#include<assert.h>
#include<ev.h>
#include<stdlib.h>

static void * allocator( void * ptr, long size ) {
	assert( ( size >= 0 ) && ( ( unsigned long ) size <= ( size_t ) -1 ) );

	return realloc( ptr, size );
}

int main( void ) {
	ev_set_allocator( allocator );

	for ( size_t i = 0; i != 1000; ++i ) {
		struct ev_loop * loop = ev_loop_new( EVFLAG_AUTO );
		if ( loop == NULL ) { /* never happens with this
					 implementation */
			break;
		}
		ev_loop_destroy( loop );
	}

	return 0;
}
_______________________________________________
libev mailing list
[email protected]
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev

Reply via email to