ctask.pas 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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. procedure rebuild_hash;
  45. public
  46. constructor create;
  47. destructor destroy; override;
  48. // Find the task for module m
  49. function findtask(m : tmodule) : ttask_list;
  50. // Can we continue processing this module ? If not, firstwaiting contains first module that m is waiting for.
  51. function cancontinue(m : tmodule; checksub : boolean; out firstwaiting: tmodule): boolean;
  52. // Overload of cancontinue, based on task.
  53. function cancontinue(t: ttask_list; out firstwaiting: tmodule): boolean; inline;
  54. // Continue processing this module. Return true if the module is done and can be removed.
  55. function continue(t : ttask_list): Boolean;
  56. // process the queue. Note that while processing the queue, elements will be added.
  57. procedure processqueue;
  58. // add a module to the queue. If a module is already in the queue, we do not add it again.
  59. procedure addmodule(m : tmodule);
  60. end;
  61. var
  62. task_handler : TTask_handler;
  63. procedure InitTaskHandler;
  64. procedure DoneTaskHandler;
  65. implementation
  66. uses verbose, fppu, finput, globtype, sysutils, scanner, parser, pmodules;
  67. procedure InitTaskHandler;
  68. begin
  69. task_handler:=ttask_handler.create;
  70. schedule_recompile_proc:=@task_handler.addmodule;
  71. end;
  72. procedure DoneTaskHandler;
  73. begin
  74. freeandnil(task_handler);
  75. end;
  76. { ttasklinkedlist }
  77. function ttasklinkedlist.firsttask: ttask_list;
  78. begin
  79. Result:=ttask_list(first);
  80. end;
  81. { ttask_list }
  82. constructor ttask_list.create(_m: tmodule);
  83. begin
  84. inherited create;
  85. module:=_m;
  86. state:=nil;
  87. end;
  88. destructor ttask_list.destroy;
  89. begin
  90. DiscardState;
  91. Inherited;
  92. end;
  93. procedure ttask_list.DiscardState;
  94. begin
  95. FreeAndNil(state);
  96. end;
  97. function ttask_list.nexttask: ttask_list;
  98. begin
  99. Result:=ttask_list(next);
  100. end;
  101. procedure ttask_list.SaveState;
  102. begin
  103. if State=Nil then
  104. State:=tglobalstate.Create(true)
  105. else
  106. State.save(true);
  107. end;
  108. procedure ttask_list.RestoreState;
  109. begin
  110. if not module.is_reset then
  111. state.restore(true);
  112. if assigned(current_scanner) and assigned(current_scanner.inputfile) then
  113. if current_scanner.inputfile.closed then
  114. begin
  115. current_scanner.tempopeninputfile;
  116. current_scanner.gettokenpos;
  117. end;
  118. end;
  119. { ttask_handler }
  120. constructor ttask_handler.create;
  121. begin
  122. list:=ttasklinkedlist.Create;
  123. hash:=TFPHashList.Create;
  124. end;
  125. destructor ttask_handler.destroy;
  126. begin
  127. hash.free;
  128. List.Clear;
  129. FreeAndNil(list);
  130. inherited destroy;
  131. end;
  132. function ttask_handler.findtask(m: tmodule): ttask_list;
  133. begin
  134. result:=list.FirstTask;
  135. while result<>nil do
  136. begin
  137. if result.module=m then
  138. exit;
  139. result:=result.nexttask;
  140. end;
  141. {$IFDEF DEBUG_CTASK}Writeln('No task found for '+m.ToString);{$ENDIF}
  142. end;
  143. function ttask_handler.cancontinue(m: tmodule; checksub : boolean; out firstwaiting: tmodule): boolean;
  144. procedure CheckUsed(out acandidate : tmodule);
  145. var
  146. itm : TLinkedListItem;
  147. iscandidate : boolean;
  148. m2 : tmodule;
  149. begin
  150. acandidate:=nil;
  151. itm:=m.used_units.First;
  152. while (acandidate=Nil) and assigned(itm) do
  153. begin
  154. iscandidate:=Not (tused_unit(itm).u.state in [ms_processed,ms_compiled]);
  155. if iscandidate then
  156. begin
  157. acandidate:=tused_unit(itm).u;
  158. if not cancontinue(acandidate,false,m2) then
  159. acandidate:=nil;
  160. end;
  161. itm:=itm.Next;
  162. end;
  163. end;
  164. var
  165. m2 : tmodule;
  166. begin
  167. firstwaiting:=nil;
  168. // We do not need to consider the program as long as there are units that need to be treated.
  169. if (m.is_initial and not m.is_unit) and (list.count>1) then
  170. exit(False);
  171. case m.state of
  172. ms_unknown : cancontinue:=true;
  173. ms_registered : cancontinue:=true;
  174. ms_compile : cancontinue:=true;
  175. ms_compiling_waitimpl : cancontinue:=m.usedunitsloaded(false,firstwaiting);
  176. ms_compiling_waitfinish : cancontinue:=m.nowaitingforunits(firstwaiting);
  177. ms_compiling_waitintf : cancontinue:=m.usedunitsloaded(true,firstwaiting);
  178. ms_compiling_wait : cancontinue:=m.usedunitsloaded(true,firstwaiting);
  179. ms_load : cancontinue:=m.usedunitsloaded(true,firstwaiting);
  180. ms_compiled : cancontinue:=true;
  181. ms_processed : cancontinue:=true;
  182. ms_moduleerror : cancontinue:=true;
  183. { else
  184. InternalError(2024011802);}
  185. end;
  186. if (not cancontinue) and checksub then
  187. begin
  188. checkused(m2);
  189. if m2<>nil then
  190. firstwaiting:=m2;
  191. end;
  192. {$IFDEF DEBUG_CTASK}
  193. Write(m.ToString,' state: ',m.state,', can continue: ',Result);
  194. if result then
  195. Writeln
  196. else
  197. begin
  198. Write(' (First waiting: ');
  199. If Assigned(FirstWaiting) then
  200. Writeln(FirstWaiting.ToString,' )')
  201. else
  202. Writeln('<none>)');
  203. end;
  204. {$ENDIF}
  205. end;
  206. function ttask_handler.cancontinue(t : ttask_list; out firstwaiting : tmodule): boolean;
  207. begin
  208. Result:=cancontinue(t.module,true,firstwaiting);
  209. end;
  210. function ttask_handler.continue(t : ttask_list) : Boolean;
  211. var
  212. m : tmodule;
  213. orgname : shortstring;
  214. begin
  215. m:=t.module;
  216. orgname:=m.modulename^;
  217. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' Continues. State: ',m.state);{$ENDIF}
  218. if Assigned(t.state) then
  219. t.RestoreState;
  220. case m.state of
  221. ms_registered : parser.compile_module(m);
  222. ms_load : with tppumodule(m) do
  223. loadppu(reload_from);
  224. ms_compile : parser.compile_module(m);
  225. ms_compiled : if (not m.is_initial) or m.is_unit then
  226. (m as tppumodule).post_load_or_compile(m,m.compilecount>1);
  227. ms_compiling_waitintf : pmodules.parse_unit_interface_declarations(m);
  228. ms_compiling_waitimpl : pmodules.proc_unit_implementation(m);
  229. ms_compiling_waitfinish : pmodules.finish_unit(m);
  230. ms_compiling_wait : pmodules.proc_program_declarations(m,m.islibrary);
  231. ms_processed : ;
  232. else
  233. InternalError(2024011801);
  234. end;
  235. if m.state=ms_compiled then
  236. begin
  237. parsing_done(m);
  238. if m.is_initial and not m.is_unit then
  239. m.state:=ms_processed;
  240. end;
  241. Result:=m.state=ms_processed;
  242. {$IFDEF DEBUG_CTASK}
  243. Write(m.ToString,' done: ',Result);
  244. if Result then
  245. Writeln
  246. else
  247. Writeln(', state is now: ',m.state);
  248. {$ENDIF}
  249. if not result then
  250. // Not done, save state
  251. t.SaveState;
  252. {
  253. the name can change as a result of processing, e.g. PROGRAM -> TB0406
  254. Normally only for the initial module, but we'll do a generic check.
  255. }
  256. if m.modulename^<>orgname then
  257. rebuild_hash;
  258. end;
  259. procedure ttask_handler.rebuild_hash;
  260. var
  261. t : ttask_list;
  262. begin
  263. Hash.Clear;
  264. t:=list.firsttask;
  265. While assigned(t) do
  266. begin
  267. Hash.Add(t.module.modulename^,t);
  268. t:=t.nexttask;
  269. end;
  270. end;
  271. procedure ttask_handler.processqueue;
  272. var
  273. t,t2 : ttask_list;
  274. process : boolean;
  275. dummy,firstwaiting : tmodule;
  276. begin
  277. t:=list.firsttask;
  278. While t<>nil do
  279. begin
  280. process:=cancontinue(t,firstwaiting);
  281. if process then
  282. begin
  283. if continue(t) then
  284. begin
  285. {$IFDEF DEBUG_CTASK}Writeln(t.module.ToString,' is finished, removing from task list');{$ENDIF}
  286. hash.Remove(t.module);
  287. list.Remove(t);
  288. end;
  289. // maybe the strategy can be improved.
  290. t:=list.firsttask;
  291. end
  292. else if assigned(firstwaiting) and cancontinue(firstwaiting,true, dummy) then
  293. begin
  294. t2:=findtask(firstwaiting);
  295. if t2=nil then
  296. t2:=t.nexttask;
  297. t:=t2;
  298. end
  299. else
  300. begin
  301. t:=t.nexttask;
  302. end;
  303. if t=nil then
  304. t:=list.firsttask;
  305. end;
  306. end;
  307. procedure ttask_handler.addmodule(m: tmodule);
  308. var
  309. n : TSymStr;
  310. e, t : ttask_list;
  311. begin
  312. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' added to task scheduler. State: ',m.state);{$ENDIF}
  313. n:=m.modulename^;
  314. e:=ttask_list(Hash.Find(n));
  315. if e=nil then
  316. begin
  317. // Clear reset flag.
  318. // This can happen when during load, reset is done and unit is added to task list.
  319. m.is_reset:=false;
  320. t:=ttask_list.create(m);
  321. list.insert(t);
  322. hash.Add(n,t);
  323. if list.count=1 then
  324. main:=m;
  325. end
  326. else
  327. begin
  328. // We have a task, if it was reset, then clear the state and move the task to the start.
  329. if m.is_reset then
  330. begin
  331. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' was reset, resetting flag. State: ',m.state);{$ENDIF}
  332. m.is_reset:=false;
  333. t:=findtask(m);
  334. if assigned(t) then
  335. begin
  336. t.DiscardState;
  337. list.Remove(t);
  338. list.insertbefore(t,list.First);
  339. end;
  340. end;
  341. end;
  342. end;
  343. end.