Thread Local Data - Maple Help

Maple 17 has support for declaring variables that can store a different value for each thread that accesses the variable.  This allows algorithms to be implemented that need to maintain state, without needing to worrying about that state being modified by other threads.  This makes it easier to implement thread safe versions of those algorithms.

A module that wants to maintain internal state generally will not work correctly when executed in parallel. Consider the following example: the Mapper module maintains an internal state using the variables func and data.  The setMapper routine is used to set these variables. When ModuleApply is called, it maps func over the given data.

 > Mapper := module ()  local func, data;  export setMapper, ModuleApply;   setMapper := proc (f::procedure, d::anything)  func := f; data := d;  NULL end proc; ModuleApply := proc (d)  map(func, d, data)  end proc end module:

 > adder := proc ()  Mapper:-setMapper(proc (x, y) options operator, arrow; x+y end proc, 1);  Mapper([seq(i, i = 1 .. 10)]) end proc:

 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]$ (1)

 > multer := proc ()  Mapper:-setMapper(proc (x, y) options operator, arrow; x*y end proc, 3);  Mapper([seq(i, i = 1 .. 10)])  end proc:
 > multer();
 $\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (2)



If you execute the adder and multer commands in parallel, you can get incorrect results. Executing the following statement multiple times will show different results, some of which will be incorrect.

 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]$ (3)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (4)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (5)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (6)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (7)

Incorrect results are computed because the func and data state variables are shared between threads. Thus when one thread changes func's value, the other thread is also affected. In Maple 17, this can be fixed by declaring the state variables as thread_local. This means that each thread will maintain its own value for the state variables. Thus changing the value in one thread will not effect other threads.

 > MapperTL := module ()  local func::thread_local, data::thread_local;  export setMapper, ModuleApply;    setMapper := proc (f::procedure, d::anything)  func := f; data := d;  NULL  end proc;  ModuleApply := proc (d) map(func, d, data)  end proc  end module;
 ${\mathrm{MapperTL}}{:=}{\mathbf{module}}\left({}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{local}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{func}}{::}{\mathrm{thread_local}}{,}{\mathrm{data}}{::}{\mathrm{thread_local}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{export}}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{setMapper}}{,}{\mathrm{ModuleApply}}{;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end module}}$ (8)



 > adderTL := proc ()  MapperTL:-setMapper(proc (x, y) options operator, arrow; x+y end proc, 1);  MapperTL([seq(i, i = 1 .. 10)])  end proc;
 ${\mathrm{adderTL}}{:=}{\mathbf{proc}}\left({}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{MapperTL}}{:-}{\mathrm{setMapper}}{}\left(\left({x}{,}{y}\right){→}{x}{+}{y}{,}{1}\right){;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{MapperTL}}{}\left(\left[{\mathrm{seq}}{}\left({i}{,}{i}{=}{1}{..}{10}\right)\right]\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end proc}}$ (9)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]$ (10)

 > multerTL := proc ()  MapperTL:-setMapper(proc (x, y) options operator, arrow; x*y end proc, 3);  MapperTL([seq(i, i = 1 .. 10)])  end proc;
 ${\mathrm{multerTL}}{:=}{\mathbf{proc}}\left({}\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{MapperTL}}{:-}{\mathrm{setMapper}}{}\left(\left({x}{,}{y}\right){→}{x}{*}{y}{,}{3}\right){;}\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathrm{MapperTL}}{}\left(\left[{\mathrm{seq}}{}\left({i}{,}{i}{=}{1}{..}{10}\right)\right]\right)\phantom{\rule[-0.0ex]{0.5em}{0.0ex}}{\mathbf{end proc}}$ (11)
 > multerTL();
 $\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (12)

Executing this version, using the thread local variables, will return the expected result.

 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (13)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (14)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (15)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (16)
 $\left[{2}{,}{3}{,}{4}{,}{5}{,}{6}{,}{7}{,}{8}{,}{9}{,}{10}{,}{11}\right]{,}\left[{3}{,}{6}{,}{9}{,}{12}{,}{15}{,}{18}{,}{21}{,}{24}{,}{27}{,}{30}\right]$ (17)

 See Also thread_local