Skip to content
Prev 45514 / 63424 Next

parallel: Race-condition concern regarding graphics devices in a multi-thread environment

On Fri, Apr 5, 2013 at 2:44 PM, Simon Urbanek
<simon.urbanek at r-project.org> wrote:
Oh my... all this time I've thought mc* were working with threads.
...despite the 'parallel' vignette writing it in black and white: "In
principle the workers could be implemented by threads\footnote{only
`in principle' since the R interpreter is not thread-safe.} or
lightweight processes, but in the current implementation they are full
processes."  Note to self: lapply(rep(c(9,14), times=100),
fortunes::fortune)
Got it; at the moment the main process is forked, each child *process*
gets a copy of the existing list of devices (devices which they then
may interact with but that's bad style - or is ignored?!?), whereas
any newly created graphics device is unique to each child process.
Device indices are local to each child processes.   In case someone
else follows this or finds this thread later, the following example
illustrates this:

library("parallel");
jpeg("foo0.jpg");
idx <- dev.cur();
plot(1:10, main="main");

dummy <- function(i) {
  before <- dev.list();
  filename <- sprintf("foo%d.png", i);
  png(filename);
  plot(1:10, main=i);
  # No dev.off(); tests what happens if you forget
  # to close device (see below).
  list(before=before, after=dev.list());
}

res <- mclapply(1:2, FUN=dummy, mc.cores=2);
str(res);
## List of 2
##  $ :List of 2
##   ..$ before: Named int 2
##   .. ..- attr(*, "names")= chr "jpeg"
##   ..$ after : Named int [1:2] 2 3
##   .. ..- attr(*, "names")= chr [1:2] "jpeg" "png"
##  $ :List of 2
##   ..$ before: Named int 2
##   .. ..- attr(*, "names")= chr "jpeg"
##   ..$ after : Named int [1:2] 2 3
##   .. ..- attr(*, "names")= chr [1:2] "jpeg" "png"

# When child processes finishes, any open devices are closed.
str(dev.list());
##  Named int 2
## - attr(*, "names")= chr "jpeg"

# Close the opened jpeg device.
dev.off(idx);

# All devices closed
str(dev.list())
##  NULL
I was considering the general case (where I don't know), e.g.
dev.new(<user specified>).
Test/example:

dummy <- function(i) {
  x11();
  plot(1:10, main=i);
}

res <- mclapply(1:2, FUN=dummy, mc.cores=2);
## Warning message:
## In mclapply(1:2, FUN = dummy, mc.cores = 2) :
##   all scheduled cores encountered errors in user code
print(res);
## [[1]]
## [1] "Error in x11() : a forked child should not open a graphics device\n"
## [[2]]
## [1] "Error in x11() : a forked child should not open a graphics device\n"


Thanks for your help Simon,

Henrik