Major memory leak in CreateNewProc()
Posted: Sat Feb 06, 2021 2:07 pm
Check out the attached demo code. I think there's a major memory leak in CreateNewProc() when using these lines:
without those lines, there is no leak. Also when just one thread is involved, there is no leak either, i.e. if you disable this line, it doesn't leak either:
Here's the demo program. It leaks about 750kb of memory here per start.
On MorphOS and AmigaOS3 there is no leak. Only OS4 is affected. I've tested various versions of OS4.1 including the most recent version (Final Edition Update 2) and the leak happens on all of them.
Code: Select all
NP_Output, Output(),
NP_Input, Input(),
NP_CloseOutput, 0,
NP_CloseInput, 0,
Code: Select all
// disable the next line and it won't leak
flag = !flag;
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <exec/exec.h>
#include <proto/dos.h>
#include <proto/exec.h>
#define STC_STARTUP -2
#define STC_SHUTDOWN -1
struct Library *DOSBase = NULL;
struct DOSIFace *IDOS = NULL;
struct SubTaskMsg
{
struct Message stm_Message;
WORD stm_Command;
APTR stm_Parameter;
LONG stm_Result;
};
struct SubTask
{
struct Task *st_Task;
struct MsgPort *st_Port;
struct MsgPort *st_Reply;
APTR st_Data;
struct SubTaskMsg st_Message;
BPTR fh;
};
static LONG SendSubTaskMsg(struct SubTask *st, WORD command, APTR params)
{
st->st_Message.stm_Message.mn_ReplyPort = st->st_Reply;
st->st_Message.stm_Message.mn_Length = sizeof(struct SubTaskMsg);
st->st_Message.stm_Command = command;
st->st_Message.stm_Parameter = params;
st->st_Message.stm_Result = 0;
PutMsg((command == STC_STARTUP) ? &((struct Process *) st->st_Task)->pr_MsgPort : st->st_Port, (struct Message *) &st->st_Message);
WaitPort(st->st_Reply);
GetMsg(st->st_Reply);
return st->st_Message.stm_Result;
}
static APTR createproc(STRPTR name, APTR func, APTR data, APTR replyport, int stacksize, int prio)
{
struct SubTask *st;
st = AllocVec(sizeof(struct SubTask), MEMF_PUBLIC|MEMF_CLEAR);
if(st) {
st->st_Reply = replyport;
st->st_Data = data;
st->st_Task = (struct Task *) CreateNewProcTags(NP_Entry, (ULONG) func,
NP_Name, (ULONG) name,
NP_StackSize, (ULONG) stacksize,
NP_Output, Output(),
NP_Input, Input(),
NP_CloseOutput, 0,
NP_CloseInput, 0,
TAG_DONE);
if(st->st_Task) {
if(SendSubTaskMsg(st, STC_STARTUP, st)) return st;
}
FreeVec(st);
}
return NULL;
}
static void killproc(APTR handle)
{
struct SubTask *st = (struct SubTask *) handle;
SendSubTaskMsg(st, STC_SHUTDOWN, st);
FreeVec(st);
}
static void exitproc(APTR handle, int failed)
{
struct SubTask *st = (struct SubTask *) handle;
if(st->st_Port) DeleteMsgPort(st->st_Port);
if(st->fh) Close(st->fh);
// We reply after a Forbid() to make sure we're really gone when the main task continues.
Forbid();
st->st_Message.stm_Result = FALSE;
ReplyMsg((struct Message *) &st->st_Message);
}
static void setprocready(APTR handle)
{
struct SubTask *st = (struct SubTask *) handle;
st->st_Message.stm_Result = TRUE;
ReplyMsg((struct Message *) &st->st_Message);
}
static APTR initproc(APTR userdata, int debug)
{
struct Task *me = FindTask(NULL);
struct SubTask *st;
struct SubTaskMsg *stm;
WaitPort(&((struct Process *) me)->pr_MsgPort);
stm = (struct SubTaskMsg *) GetMsg(&((struct Process *)me)->pr_MsgPort);
st = (struct SubTask *) stm->stm_Parameter;
if(!(st->st_Port = CreateMsgPort())) {
exitproc(st, FALSE);
return NULL;
}
if(debug) st->fh = Open("CON:640/0/400/480/Proc Debug", MODE_NEWFILE);
return st;
}
static void prepprocexit(APTR handle)
{
struct SubTask *st = (struct SubTask *) handle;
GetMsg(st->st_Port);
}
static SAVEDS void sub_proc(void)
{
struct SubTask *st = initproc(NULL, FALSE);
ULONG quitsig;
setprocready(st);
quitsig = 1L << ((struct SubTask *) st)->st_Port->mp_SigBit;
for(;;) {
ULONG sigmask = Wait(quitsig);
if(sigmask & quitsig) break;
}
prepprocexit(st);
exitproc(st, FALSE);
}
int main(int argc, char *argv[])
{
struct MsgPort *replyport;
APTR handle[2] = {NULL, NULL};
int k, flag = 0;
DOSBase = OpenLibrary("dos.library", 0);
IDOS = (struct DOSIFace *) GetInterface(DOSBase, "main", 1, NULL);
replyport = CreateMsgPort();
for(k = 0; k < 3000; k++) {
if(!(handle[flag] = createproc("My subtask", (APTR) sub_proc, NULL, replyport, 32768, 0))) {
printf("Error!\n");
return 0;
}
// disable this line and it won't leak
flag = !flag;
if(handle[flag]) killproc(handle[flag]);
}
flag = !flag;
if(handle[flag]) killproc(handle[flag]);
DeleteMsgPort(replyport);
DropInterface((struct Interface *) IDOS);
CloseLibrary(DOSBase);
return 0;
}