Skip to content

Commit f196c9f

Browse files
author
Dimitri van Heesch
committed
Bug 756241 - Race condition in parallel DOT runs
1 parent ffff695 commit f196c9f

File tree

3 files changed

+77
-55
lines changed

3 files changed

+77
-55
lines changed

qtools/Doxyfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ PERL_PATH = /usr/bin/perl
279279
CLASS_DIAGRAMS = YES
280280
MSCGEN_PATH =
281281
HIDE_UNDOC_RELATIONS = YES
282-
HAVE_DOT = NO
282+
HAVE_DOT = YES
283283
DOT_NUM_THREADS = 0
284284
DOT_FONTNAME =
285285
DOT_FONTSIZE = 10
@@ -306,4 +306,4 @@ MAX_DOT_GRAPH_DEPTH = 0
306306
DOT_TRANSPARENT = YES
307307
DOT_MULTI_TARGETS = NO
308308
GENERATE_LEGEND = YES
309-
DOT_CLEANUP = NO
309+
DOT_CLEANUP = YES

src/dot.cpp

+38-44
Original file line numberDiff line numberDiff line change
@@ -659,10 +659,9 @@ static bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
659659

660660
// since dot silently reproduces the input file when it does not
661661
// support the PNG format, we need to check the result.
662-
static void checkDotResult(const QCString &imgName)
662+
static void checkDotResult(const char *imgExt, const char *imgName)
663663
{
664-
QCString imgExt = getDotImageExtension();
665-
if (imgExt=="png")
664+
if (qstrcmp(imgExt,"png")==0)
666665
{
667666
FILE *f = portable_fopen(imgName,"rb");
668667
if (f)
@@ -675,19 +674,19 @@ static void checkDotResult(const QCString &imgName)
675674
err("Image `%s' produced by dot is not a valid PNG!\n"
676675
"You should either select a different format "
677676
"(DOT_IMAGE_FORMAT in the config file) or install a more "
678-
"recent version of graphviz (1.7+)\n",imgName.data()
677+
"recent version of graphviz (1.7+)\n",imgName
679678
);
680679
}
681680
}
682681
else
683682
{
684-
err("Could not read image `%s' generated by dot!\n",imgName.data());
683+
err("Could not read image `%s' generated by dot!\n",imgName);
685684
}
686685
fclose(f);
687686
}
688687
else
689688
{
690-
err("Could not open image `%s' generated by dot!\n",imgName.data());
689+
err("Could not open image `%s' generated by dot!\n",imgName);
691690
}
692691
}
693692
}
@@ -793,54 +792,46 @@ int DotNodeList::compareValues(const DotNode *n1,const DotNode *n2) const
793792

794793
DotRunner::DotRunner(const QCString &file,const QCString &path,
795794
bool checkResult,const QCString &imageName)
796-
: m_file(file), m_path(path),
797-
m_checkResult(checkResult), m_imageName(imageName)
798-
{
799-
static bool dotCleanUp = Config_getBool("DOT_CLEANUP");
800-
m_cleanUp = dotCleanUp;
795+
: m_dotExe(Config_getString("DOT_PATH")+"dot"),
796+
m_file(file), m_path(path),
797+
m_checkResult(checkResult), m_imageName(imageName),
798+
m_imgExt(getDotImageExtension())
799+
{
800+
static bool dotCleanUp = Config_getBool("DOT_CLEANUP");
801+
static bool dotMultiTargets = Config_getBool("DOT_MULTI_TARGETS");
802+
m_cleanUp = dotCleanUp;
803+
m_multiTargets = dotMultiTargets;
801804
m_jobs.setAutoDelete(TRUE);
802805
}
803806

804807
void DotRunner::addJob(const char *format,const char *output)
805808
{
806809
QCString args = QCString("-T")+format+" -o \""+output+"\"";
807-
m_jobs.append(new QCString(args));
810+
m_jobs.append(new DotConstString(args));
808811
}
809812

810813
void DotRunner::addPostProcessing(const char *cmd,const char *args)
811814
{
812-
m_postCmd = cmd;
813-
m_postArgs = args;
815+
m_postCmd.set(cmd);
816+
m_postArgs.set(args);
814817
}
815818

816819
bool DotRunner::run()
817820
{
818821
int exitCode=0;
819-
// we need to use data here to make a copy of the string, as Config_getString can be called by
820-
// multiple threads simulaneously and the reference counting is not thread safe.
821-
QCString dotExe = Config_getString("DOT_PATH").data();
822-
dotExe+="dot";
823822

824-
bool multiTargets = Config_getBool("DOT_MULTI_TARGETS");
825823
QCString dotArgs;
826-
QListIterator<QCString> li(m_jobs);
827-
QCString *s;
828-
QCString file = m_file;
829-
QCString path = m_path;
830-
QCString imageName = m_imageName;
831-
QCString postCmd = m_postCmd;
832-
QCString postArgs = m_postArgs;
833-
bool checkResult = m_checkResult;
834-
bool cleanUp = m_cleanUp;
835-
if (multiTargets)
836-
{
837-
dotArgs="\""+file+"\"";
824+
QListIterator<DotConstString> li(m_jobs);
825+
DotConstString *s;
826+
if (m_multiTargets)
827+
{
828+
dotArgs=QCString("\"")+m_file.data()+"\"";
838829
for (li.toFirst();(s=li.current());++li)
839830
{
840831
dotArgs+=' ';
841-
dotArgs+=*s;
832+
dotArgs+=s->data();
842833
}
843-
if ((exitCode=portable_system(dotExe,dotArgs,FALSE))!=0)
834+
if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0)
844835
{
845836
goto error;
846837
}
@@ -849,30 +840,33 @@ bool DotRunner::run()
849840
{
850841
for (li.toFirst();(s=li.current());++li)
851842
{
852-
dotArgs="\""+file+"\" "+*s;
853-
if ((exitCode=portable_system(dotExe,dotArgs,FALSE))!=0)
843+
dotArgs=QCString("\"")+m_file.data()+"\" "+s->data();
844+
if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0)
854845
{
855846
goto error;
856847
}
857848
}
858849
}
859-
if (!postCmd.isEmpty() && portable_system(postCmd,postArgs)!=0)
850+
if (!m_postCmd.isEmpty() && portable_system(m_postCmd.data(),m_postArgs.data())!=0)
860851
{
861852
err("Problems running '%s' as a post-processing step for dot output\n",m_postCmd.data());
862853
return FALSE;
863854
}
864-
if (checkResult) checkDotResult(imageName);
865-
if (cleanUp)
855+
if (m_checkResult)
856+
{
857+
checkDotResult(m_imgExt.data(),m_imageName.data());
858+
}
859+
if (m_cleanUp)
866860
{
867861
//printf("removing dot file %s\n",m_file.data());
868862
//QDir(path).remove(file);
869-
m_cleanupItem.file = file;
870-
m_cleanupItem.path = path;
863+
m_cleanupItem.file.set(m_file.data());
864+
m_cleanupItem.path.set(m_path.data());
871865
}
872866
return TRUE;
873867
error:
874868
err("Problems running dot: exit code=%d, command='%s', arguments='%s'\n",
875-
exitCode,dotExe.data(),dotArgs.data());
869+
exitCode,m_dotExe.data(),dotArgs.data());
876870
return FALSE;
877871
}
878872

@@ -1202,7 +1196,7 @@ void DotWorkerThread::run()
12021196
while ((runner=m_queue->dequeue()))
12031197
{
12041198
runner->run();
1205-
DotRunner::CleanupItem cleanup = runner->cleanup();
1199+
const DotRunner::CleanupItem &cleanup = runner->cleanup();
12061200
if (!cleanup.file.isEmpty())
12071201
{
12081202
m_cleanupItems.append(new DotRunner::CleanupItem(cleanup));
@@ -1216,7 +1210,7 @@ void DotWorkerThread::cleanup()
12161210
DotRunner::CleanupItem *ci;
12171211
for (;(ci=it.current());++it)
12181212
{
1219-
QDir(ci->path).remove(ci->file);
1213+
QDir(ci->path.data()).remove(ci->file.data());
12201214
}
12211215
}
12221216

@@ -4237,7 +4231,7 @@ void writeDotGraphFromFile(const char *inFile,const char *outDir,
42374231
return;
42384232
}
42394233

4240-
if (format==GOF_BITMAP) checkDotResult(absImgName);
4234+
if (format==GOF_BITMAP) checkDotResult(getDotImageExtension(),absImgName);
42414235

42424236
Doxygen::indexList->addImageFile(imgName);
42434237

src/dot.h

+37-9
Original file line numberDiff line numberDiff line change
@@ -326,15 +326,40 @@ class DotGroupCollaboration
326326
QList<Edge> m_edges;
327327
};
328328

329+
/** Minimal constant string class that is thread safe, once initialized. */
330+
class DotConstString
331+
{
332+
public:
333+
DotConstString() { m_str=0; }
334+
~DotConstString() { delete[] m_str; }
335+
DotConstString(const QCString &s) : m_str(0) { set(s); }
336+
DotConstString(const DotConstString &s) : m_str(0) { set(s.data()); }
337+
const char *data() const { return m_str; }
338+
bool isEmpty() const { return m_str==0 || m_str[0]=='\0'; }
339+
void set(const QCString &s)
340+
{
341+
delete[] m_str;
342+
m_str=0;
343+
if (!s.isEmpty())
344+
{
345+
m_str=new char[s.length()+1];
346+
qstrcpy(m_str,s.data());
347+
}
348+
}
349+
private:
350+
DotConstString &operator=(const DotConstString &);
351+
char *m_str;
352+
};
353+
329354
/** Helper class to run dot from doxygen.
330355
*/
331356
class DotRunner
332357
{
333358
public:
334359
struct CleanupItem
335360
{
336-
QCString path;
337-
QCString file;
361+
DotConstString path;
362+
DotConstString file;
338363
};
339364

340365
/** Creates a runner for a dot \a file. */
@@ -352,16 +377,19 @@ class DotRunner
352377

353378
/** Runs dot for all jobs added. */
354379
bool run();
355-
CleanupItem cleanup() const { return m_cleanupItem; }
380+
const CleanupItem &cleanup() const { return m_cleanupItem; }
356381

357382
private:
358-
QList<QCString> m_jobs;
359-
QCString m_postArgs;
360-
QCString m_postCmd;
361-
QCString m_file;
362-
QCString m_path;
383+
DotConstString m_dotExe;
384+
bool m_multiTargets;
385+
QList<DotConstString> m_jobs;
386+
DotConstString m_postArgs;
387+
DotConstString m_postCmd;
388+
DotConstString m_file;
389+
DotConstString m_path;
363390
bool m_checkResult;
364-
QCString m_imageName;
391+
DotConstString m_imageName;
392+
DotConstString m_imgExt;
365393
bool m_cleanUp;
366394
CleanupItem m_cleanupItem;
367395
};

0 commit comments

Comments
 (0)