ObjFW  Artifact [99568ccb12]

Artifact 99568ccb12d5b07dbece1f3af77cb153a11007cda8f2f4fee9c32e82cd51ca9c:

  • File src/runtime/autorelease.m — part of check-in [e7f4f80e23] at 2020-01-24 03:03:32 on branch trunk — runtime: Correctly handle AR pool push during pop

    Getting a pointer and increasing it until we reach the top pointer does
    not work: Releasing an object can temporarily create new autorelease
    pools, which can trigger resizing of "objects" using realloc, which can
    move it to a different address, which will then lead to continuing to
    iterate on a now invalid pointer.

    This is now solved by using an index into "objects" instead. Since we're
    now indexing for the pop, let's use indexes everywhere, as they're more
    readable anyway.

    While debugging this, I noticed that the last pool is popped quite
    frequently, only for a new pool to be pushed immediately again. This
    resulted in a free followed by a malloc every time. Instead, keep the
    pool, but let OFThread explicitly say when to free everything. (user: js, size: 3222) [annotate] [blame] [check-ins using] [more...]


/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
 *               2018, 2019, 2020
 *   Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>

#import "ObjFWRT.h"
#import "private.h"

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
# import "tlskey.h"
#endif

#if defined(OF_HAVE_COMPILER_TLS)
static thread_local id *objects = NULL;
static thread_local uintptr_t count = 0;
static thread_local uintptr_t size = 0;
#elif defined(OF_HAVE_THREADS)
static of_tlskey_t objectsKey, countKey, sizeKey;
#else
static id *objects = NULL;
static uintptr_t count = 0;
static uintptr_t size = 0;
#endif

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
OF_CONSTRUCTOR()
{
	OF_ENSURE(of_tlskey_new(&objectsKey));
	OF_ENSURE(of_tlskey_new(&countKey));
	OF_ENSURE(of_tlskey_new(&sizeKey));
}
#endif

void *
objc_autoreleasePoolPush()
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
#endif
	return (void *)count;
}

void
objc_autoreleasePoolPop(void *pool)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *objects = of_tlskey_get(objectsKey);
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
#endif
	uintptr_t idx = (uintptr_t)pool;
	bool freeMem = false;

	if (idx == (uintptr_t)-1) {
		idx++;
		freeMem = true;
	}

	for (uintptr_t i = idx; i < count; i++) {
		[objects[i] release];

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		objects = of_tlskey_get(objectsKey);
		count = (uintptr_t)of_tlskey_get(countKey);
#endif
	}

	count = idx;

	if (freeMem) {
		free(objects);
		objects = NULL;
#if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
		size = 0;
#else
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)0));
#endif
	}

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(countKey, (void *)count));
#endif
}

id
_objc_rootAutorelease(id object)
{
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	id *objects = of_tlskey_get(objectsKey);
	uintptr_t count = (uintptr_t)of_tlskey_get(countKey);
	uintptr_t size = (uintptr_t)of_tlskey_get(sizeKey);
#endif

	if (count >= size) {
		if (size == 0)
			size = 16;
		else
			size *= 2;

		OF_ENSURE((objects =
		    realloc(objects, size * sizeof(id))) != NULL);

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
		OF_ENSURE(of_tlskey_set(objectsKey, objects));
		OF_ENSURE(of_tlskey_set(sizeKey, (void *)size));
#endif
	}

	objects[count++] = object;

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	OF_ENSURE(of_tlskey_set(countKey, (void *)count));
#endif

	return object;
}