ctask.pas 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. {
  2. Copyright (c) 2024- by Michael Van Canneyt
  3. This unit handles the compiler tasks.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit ctask;
  18. {$mode ObjFPC}
  19. { $DEFINE DEBUG_CTASK}
  20. interface
  21. uses
  22. fmodule, cclasses, globstat;
  23. type
  24. { ttask_list }
  25. ttask_list = class(tlinkedlistitem)
  26. module : tmodule;
  27. state : tglobalstate;
  28. constructor create(_m : tmodule);
  29. destructor destroy; override;
  30. procedure SaveState;
  31. Procedure RestoreState;
  32. procedure DiscardState;
  33. function nexttask : ttask_list; inline;
  34. end;
  35. ttasklinkedlist = class(tlinkedlist)
  36. function firsttask : ttask_list; inline;
  37. end;
  38. { ttask_handler }
  39. ttask_handler = class
  40. private
  41. list : ttasklinkedlist;
  42. hash : TFPHashList;
  43. main : tmodule;
  44. public
  45. constructor create;
  46. destructor destroy; override;
  47. // Find the task for module m
  48. function findtask(m : tmodule) : ttask_list;
  49. // Can we continue processing this module ? If not, firstwaiting contains first module that m is waiting for.
  50. function cancontinue(m : tmodule; checksub : boolean; out firstwaiting: tmodule): boolean;
  51. // Overload of cancontinue, based on task.
  52. function cancontinue(t: ttask_list; out firstwaiting: tmodule): boolean; inline;
  53. // Continue processing this module. Return true if the module is done and can be removed.
  54. function continue(t : ttask_list): Boolean;
  55. // process the queue. Note that while processing the queue, elements will be added.
  56. procedure processqueue;
  57. // add a module to the queue. If a module is already in the queue, we do not add it again.
  58. procedure addmodule(m : tmodule);
  59. end;
  60. var
  61. task_handler : TTask_handler;
  62. procedure InitTaskHandler;
  63. procedure DoneTaskHandler;
  64. implementation
  65. uses verbose, fppu, finput, globtype, sysutils, scanner, parser, pmodules;
  66. procedure InitTaskHandler;
  67. begin
  68. task_handler:=ttask_handler.create;
  69. end;
  70. procedure DoneTaskHandler;
  71. begin
  72. freeandnil(task_handler);
  73. end;
  74. { ttasklinkedlist }
  75. function ttasklinkedlist.firsttask: ttask_list;
  76. begin
  77. Result:=ttask_list(first);
  78. end;
  79. { ttask_list }
  80. constructor ttask_list.create(_m: tmodule);
  81. begin
  82. inherited create;
  83. module:=_m;
  84. state:=nil;
  85. end;
  86. destructor ttask_list.destroy;
  87. begin
  88. DiscardState;
  89. Inherited;
  90. end;
  91. procedure ttask_list.DiscardState;
  92. begin
  93. FreeAndNil(state);
  94. end;
  95. function ttask_list.nexttask: ttask_list;
  96. begin
  97. Result:=ttask_list(next);
  98. end;
  99. procedure ttask_list.SaveState;
  100. begin
  101. if State=Nil then
  102. State:=tglobalstate.Create(true)
  103. else
  104. State.save(true);
  105. end;
  106. procedure ttask_list.RestoreState;
  107. begin
  108. if not module.is_reset then
  109. state.restore(true);
  110. if assigned(current_scanner) and assigned(current_scanner.inputfile) then
  111. if current_scanner.inputfile.closed then
  112. begin
  113. current_scanner.tempopeninputfile;
  114. current_scanner.gettokenpos;
  115. end;
  116. end;
  117. { ttask_handler }
  118. constructor ttask_handler.create;
  119. begin
  120. list:=ttasklinkedlist.Create;
  121. hash:=TFPHashList.Create;
  122. end;
  123. destructor ttask_handler.destroy;
  124. begin
  125. hash.free;
  126. List.Clear;
  127. FreeAndNil(list);
  128. inherited destroy;
  129. end;
  130. function ttask_handler.findtask(m: tmodule): ttask_list;
  131. begin
  132. result:=list.FirstTask;
  133. while result<>nil do
  134. begin
  135. if result.module=m then
  136. exit;
  137. result:=result.nexttask;
  138. end;
  139. {$IFDEF DEBUG_CTASK}Writeln('No task found for '+m.ToString);{$ENDIF}
  140. end;
  141. function ttask_handler.cancontinue(m: tmodule; checksub : boolean; out firstwaiting: tmodule): boolean;
  142. procedure CheckUsed(out acandidate : tmodule);
  143. var
  144. itm : TLinkedListItem;
  145. iscandidate : boolean;
  146. m2 : tmodule;
  147. begin
  148. acandidate:=nil;
  149. itm:=m.used_units.First;
  150. while (acandidate=Nil) and assigned(itm) do
  151. begin
  152. iscandidate:=Not (tused_unit(itm).u.state in [ms_compiled]);
  153. if iscandidate then
  154. begin
  155. acandidate:=tused_unit(itm).u;
  156. if not cancontinue(acandidate,false,m2) then
  157. acandidate:=nil;
  158. end;
  159. itm:=itm.Next;
  160. end;
  161. end;
  162. var
  163. m2 : tmodule;
  164. begin
  165. firstwaiting:=nil;
  166. if m.is_initial and (list.count>1) then
  167. exit(False);
  168. case m.state of
  169. ms_unknown : cancontinue:=true;
  170. ms_registered : cancontinue:=true;
  171. ms_compile : cancontinue:=true;
  172. ms_compiling_waitimpl : cancontinue:=m.usedunitsloaded(false,firstwaiting);
  173. ms_compiling_waitfinish : cancontinue:=m.nowaitingforunits;
  174. ms_compiling_waitintf : cancontinue:=m.usedunitsloaded(true,firstwaiting);
  175. ms_compiling_wait : cancontinue:=m.usedunitsloaded(true,firstwaiting);
  176. ms_compiled : cancontinue:=true;
  177. ms_processed : cancontinue:=true;
  178. ms_moduleerror : cancontinue:=true;
  179. else
  180. InternalError(2024011802);
  181. end;
  182. if (not cancontinue) and checksub then
  183. begin
  184. checkused(m2);
  185. if m2<>nil then
  186. firstwaiting:=m2;
  187. end;
  188. end;
  189. function ttask_handler.cancontinue(t : ttask_list; out firstwaiting : tmodule): boolean;
  190. begin
  191. Result:=cancontinue(t.module,true,firstwaiting);
  192. end;
  193. function ttask_handler.continue(t : ttask_list) : Boolean;
  194. var
  195. m : tmodule;
  196. begin
  197. m:=t.module;
  198. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' Continues. State: ',m.state);{$ENDIF}
  199. if Assigned(t.state) then
  200. t.RestoreState;
  201. case m.state of
  202. ms_registered : parser.compile_module(m);
  203. ms_compile : parser.compile_module(m);
  204. ms_compiled : if (not m.is_initial) or m.is_unit then
  205. (m as tppumodule).post_load_or_compile(m.compilecount>1);
  206. ms_compiling_waitintf : pmodules.parse_unit_interface_declarations(m);
  207. ms_compiling_waitimpl : pmodules.proc_unit_implementation(m);
  208. ms_compiling_waitfinish : pmodules.proc_unit_implementation(m);
  209. ms_compiling_wait : pmodules.proc_program_declarations(m,m.islibrary);
  210. ms_processed : ;
  211. else
  212. InternalError(2024011801);
  213. end;
  214. if m.state=ms_compiled then
  215. begin
  216. parsing_done(m);
  217. if m.is_initial and not m.is_unit then
  218. m.state:=ms_processed;
  219. end;
  220. Result:=m.state=ms_processed;
  221. {$IFDEF DEBUG_CTASK}
  222. Write(m.ToString,' done: ',Result);
  223. if Result then
  224. Writeln
  225. else
  226. Writeln(', state is now: ',m.state);
  227. {$ENDIF}
  228. if not result then
  229. // Not done, save state
  230. t.SaveState;
  231. end;
  232. procedure ttask_handler.processqueue;
  233. var
  234. t,t2 : ttask_list;
  235. process : boolean;
  236. dummy,firstwaiting : tmodule;
  237. begin
  238. t:=list.firsttask;
  239. While t<>nil do
  240. begin
  241. process:=cancontinue(t,firstwaiting);
  242. if process then
  243. begin
  244. if continue(t) then
  245. begin
  246. {$IFDEF DEBUG_CTASK}Writeln(t.module.ToString,' is finished, removing from task list');{$ENDIF}
  247. hash.Remove(t.module);
  248. list.Remove(t);
  249. end;
  250. // maybe the strategy can be improved.
  251. t:=list.firsttask;
  252. end
  253. else if assigned(firstwaiting) and cancontinue(firstwaiting,true, dummy) then
  254. begin
  255. t2:=findtask(firstwaiting);
  256. if t2=nil then
  257. t2:=t.nexttask;
  258. t:=t2;
  259. end
  260. else
  261. begin
  262. t:=t.nexttask;
  263. end;
  264. end;
  265. end;
  266. procedure ttask_handler.addmodule(m: tmodule);
  267. var
  268. n : TSymStr;
  269. e : tmodule;
  270. t : ttask_list;
  271. begin
  272. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' added to task scheduler. State: ',m.state);{$ENDIF}
  273. n:=m.modulename^;
  274. e:=tmodule(Hash.Find(n));
  275. if e=nil then
  276. begin
  277. t:=ttask_list.create(m);
  278. list.insert(t);
  279. hash.Add(n,t);
  280. if list.count=1 then
  281. main:=m;
  282. end
  283. else
  284. begin
  285. // We have a task, if it was reset, then clear the state and move the task to the start.
  286. if m.is_reset then
  287. begin
  288. m.is_reset:=false;
  289. t:=findtask(m);
  290. if assigned(t) then
  291. begin
  292. t.DiscardState;
  293. list.Remove(t);
  294. list.insertbefore(t,list.First);
  295. end;
  296. end;
  297. end;
  298. end;
  299. end.