Skip to content

Commit 422a21c

Browse files
peffgitster
authored andcommitted
tempfile: remove deactivated list entries
Once a "struct tempfile" is added to the global cleanup list, it is never removed. This means that its storage must remain valid for the lifetime of the program. For single-use tempfiles and locks, this isn't a big deal: we just declare the struct static. But for library code which may take multiple simultaneous locks (like the ref code), they're forced to allocate a struct on the heap and leak it. This is mostly OK in practice. The size of the leak is bounded by the number of refs, and most programs exit after operating on a fixed number of refs (and allocate simultaneous memory proportional to the number of ref updates in the first place). But: 1. It isn't hard to imagine a real leak: a program which runs for a long time taking a series of ref update instructions and fulfilling them one by one. I don't think we have such a program now, but it's certainly plausible. 2. The leaked entries appear as false positives to tools like valgrind. Let's relax this rule by keeping only "active" tempfiles on the list. We can do this easily by moving the list-add operation from prepare_tempfile_object to activate_tempfile, and adding a deletion in deactivate_tempfile. Existing callers do not need to be updated immediately. They'll continue to leak any tempfile objects they may have allocated, but that's no different than the status quo. We can clean them up individually. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 24d8218 commit 422a21c

File tree

2 files changed

+25
-36
lines changed

2 files changed

+25
-36
lines changed

tempfile.c

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@
4242
* states in which this condition doesn't hold). Client code should
4343
* *not* rely on the filename being empty in this state.
4444
* - `fd` is -1 and `fp` is `NULL`
45-
* - the object is left registered in the `tempfile_list`, and
46-
* `on_list` is set.
45+
* - the object is removed from `tempfile_list` (but could be used again)
4746
*
4847
* A temporary file is owned by the process that created it. The
4948
* `tempfile` has an `owner` field that records the owner's PID. This
@@ -92,36 +91,30 @@ static void remove_tempfiles_on_signal(int signo)
9291
raise(signo);
9392
}
9493

95-
/*
96-
* Initialize *tempfile if necessary and add it to tempfile_list.
97-
*/
9894
static void prepare_tempfile_object(struct tempfile *tempfile)
9995
{
100-
if (volatile_list_empty(&tempfile_list)) {
101-
/* One-time initialization */
102-
sigchain_push_common(remove_tempfiles_on_signal);
103-
atexit(remove_tempfiles_on_exit);
104-
}
105-
106-
if (is_tempfile_active(tempfile))
107-
BUG("prepare_tempfile_object called for active object");
108-
if (!tempfile->on_list) {
109-
/* Initialize *tempfile and add it to tempfile_list: */
110-
tempfile->fd = -1;
111-
tempfile->fp = NULL;
112-
tempfile->active = 0;
113-
tempfile->owner = 0;
114-
strbuf_init(&tempfile->filename, 0);
115-
volatile_list_add(&tempfile->list, &tempfile_list);
116-
tempfile->on_list = 1;
117-
} else if (tempfile->filename.len) {
118-
/* This shouldn't happen, but better safe than sorry. */
119-
BUG("prepare_tempfile_object called for improperly-reset object");
120-
}
96+
tempfile->fd = -1;
97+
tempfile->fp = NULL;
98+
tempfile->active = 0;
99+
tempfile->owner = 0;
100+
INIT_LIST_HEAD(&tempfile->list);
101+
strbuf_init(&tempfile->filename, 0);
121102
}
122103

123104
static void activate_tempfile(struct tempfile *tempfile)
124105
{
106+
static int initialized;
107+
108+
if (is_tempfile_active(tempfile))
109+
BUG("activate_tempfile called for active object");
110+
111+
if (!initialized) {
112+
sigchain_push_common(remove_tempfiles_on_signal);
113+
atexit(remove_tempfiles_on_exit);
114+
initialized = 1;
115+
}
116+
117+
volatile_list_add(&tempfile->list, &tempfile_list);
125118
tempfile->owner = getpid();
126119
tempfile->active = 1;
127120
}
@@ -130,6 +123,7 @@ static void deactivate_tempfile(struct tempfile *tempfile)
130123
{
131124
tempfile->active = 0;
132125
strbuf_release(&tempfile->filename);
126+
volatile_list_del(&tempfile->list);
133127
}
134128

135129
/* Make sure errno contains a meaningful value on error */

tempfile.h

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,9 @@
1717
*
1818
* The caller:
1919
*
20-
* * Allocates a `struct tempfile` either as a static variable or on
21-
* the heap, initialized to zeros. Once you use the structure to
22-
* call `create_tempfile()`, it belongs to the tempfile subsystem
23-
* and its storage must remain valid throughout the life of the
24-
* program (i.e. you cannot use an on-stack variable to hold this
25-
* structure).
20+
* * Allocates a `struct tempfile`. Once the structure is passed to
21+
* `create_tempfile()`, its storage must remain valid until
22+
* `delete_tempfile()` or `rename_tempfile()` is called on it.
2623
*
2724
* * Attempts to create a temporary file by calling
2825
* `create_tempfile()`.
@@ -52,9 +49,8 @@
5249
* temporary file by calling `close_tempfile_gently()`, and later call
5350
* `delete_tempfile()` or `rename_tempfile()`.
5451
*
55-
* Even after the temporary file is renamed or deleted, the `tempfile`
56-
* object must not be freed or altered by the caller. However, it may
57-
* be reused; just pass it to another call of `create_tempfile()`.
52+
* After the temporary file is renamed or deleted, the `tempfile`
53+
* object may be reused or freed.
5854
*
5955
* If the program exits before `rename_tempfile()` or
6056
* `delete_tempfile()` is called, an `atexit(3)` handler will close
@@ -88,7 +84,6 @@ struct tempfile {
8884
volatile int fd;
8985
FILE *volatile fp;
9086
volatile pid_t owner;
91-
char on_list;
9287
struct strbuf filename;
9388
};
9489

0 commit comments

Comments
 (0)