ctask.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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. // write current queue and what is waiting for what
  61. procedure write_queue;
  62. end;
  63. var
  64. task_handler : TTask_handler;
  65. procedure InitTaskHandler;
  66. procedure DoneTaskHandler;
  67. implementation
  68. uses
  69. verbose, fppu, finput, globtype, sysutils,
  70. scanner, parser, pmodules, symbase;
  71. procedure InitTaskHandler;
  72. begin
  73. task_handler:=ttask_handler.create;
  74. end;
  75. procedure DoneTaskHandler;
  76. begin
  77. freeandnil(task_handler);
  78. end;
  79. { ttasklinkedlist }
  80. function ttasklinkedlist.firsttask: ttask_list;
  81. begin
  82. Result:=ttask_list(first);
  83. end;
  84. { ttask_list }
  85. constructor ttask_list.create(_m: tmodule);
  86. begin
  87. inherited create;
  88. module:=_m;
  89. state:=nil;
  90. end;
  91. destructor ttask_list.destroy;
  92. begin
  93. DiscardState;
  94. Inherited;
  95. end;
  96. procedure ttask_list.DiscardState;
  97. begin
  98. FreeAndNil(state);
  99. end;
  100. function ttask_list.nexttask: ttask_list;
  101. begin
  102. Result:=ttask_list(next);
  103. end;
  104. procedure ttask_list.SaveState;
  105. begin
  106. if State=Nil then
  107. State:=tglobalstate.Create(true)
  108. else
  109. State.save(true);
  110. end;
  111. procedure ttask_list.RestoreState;
  112. begin
  113. if not module.is_reset then
  114. state.restore(true);
  115. if assigned(current_scanner) and assigned(current_scanner.inputfile) then
  116. if current_scanner.inputfile.closed then
  117. begin
  118. current_scanner.tempopeninputfile;
  119. current_scanner.gettokenpos;
  120. end;
  121. end;
  122. { ttask_handler }
  123. constructor ttask_handler.create;
  124. begin
  125. list:=ttasklinkedlist.Create;
  126. hash:=TFPHashList.Create;
  127. end;
  128. destructor ttask_handler.destroy;
  129. begin
  130. hash.free;
  131. hash := nil;
  132. List.Clear;
  133. FreeAndNil(list);
  134. inherited destroy;
  135. end;
  136. function ttask_handler.findtask(m: tmodule): ttask_list;
  137. begin
  138. result:=list.FirstTask;
  139. while result<>nil do
  140. begin
  141. if result.module=m then
  142. exit;
  143. result:=result.nexttask;
  144. end;
  145. {$IFDEF DEBUG_CTASK}Writeln('No task found for '+m.ToString);{$ENDIF}
  146. end;
  147. function ttask_handler.cancontinue(m: tmodule; checksub : boolean; out firstwaiting: tmodule): boolean;
  148. procedure CheckUsed(out acandidate : tmodule);
  149. var
  150. itm : TLinkedListItem;
  151. iscandidate : boolean;
  152. m2 : tmodule;
  153. begin
  154. acandidate:=nil;
  155. itm:=m.used_units.First;
  156. while assigned(itm) do
  157. begin
  158. iscandidate:=Not (tused_unit(itm).u.state in [ms_processed,ms_compiled]);
  159. if iscandidate then
  160. begin
  161. acandidate:=tused_unit(itm).u;
  162. if cancontinue(acandidate,false,m2) then
  163. break;
  164. end;
  165. itm:=itm.Next;
  166. end;
  167. acandidate:=nil;
  168. end;
  169. var
  170. m2 : tmodule;
  171. begin
  172. firstwaiting:=nil;
  173. // We do not need to consider the program as long as there are units that need to be treated.
  174. if (m.is_initial and not m.is_unit) and (list.count>1) then
  175. exit(False);
  176. case m.state of
  177. ms_unknown : cancontinue:=true;
  178. ms_registered : cancontinue:=true;
  179. ms_compile : cancontinue:=true;
  180. ms_compiling_wait : cancontinue:=m.usedunitsloaded(true,firstwaiting);
  181. ms_compiling_waitintf : cancontinue:=m.usedunitsloaded(true,firstwaiting);
  182. ms_compiling_waitimpl : cancontinue:=m.usedunitsloaded(false,firstwaiting);
  183. ms_compiling_waitfinish : cancontinue:=m.nowaitingforunits(firstwaiting);
  184. ms_compiled_waitcrc : cancontinue:=m.usedunitsfinalcrc(firstwaiting);
  185. ms_compiled : cancontinue:=true;
  186. ms_processed : cancontinue:=true;
  187. ms_moduleerror : cancontinue:=true;
  188. else
  189. InternalError(2024011802);
  190. end;
  191. if (not cancontinue) and checksub then
  192. begin
  193. checkused(m2);
  194. if m2<>nil then
  195. firstwaiting:=m2;
  196. end;
  197. {$IFDEF DEBUG_CTASK}
  198. Write(m.ToString,' state: ',m.state,', can continue: ',Result);
  199. if result then
  200. Writeln
  201. else
  202. begin
  203. Write(' (First waiting: ');
  204. If Assigned(FirstWaiting) then
  205. Writeln(FirstWaiting.ToString,' )')
  206. else
  207. Writeln('<none>)');
  208. end;
  209. {$ENDIF}
  210. end;
  211. function ttask_handler.cancontinue(t : ttask_list; out firstwaiting : tmodule): boolean;
  212. begin
  213. Result:=cancontinue(t.module,true,firstwaiting);
  214. end;
  215. function ttask_handler.continue(t : ttask_list) : Boolean;
  216. var
  217. m : tmodule;
  218. orgname : shortstring;
  219. begin
  220. m:=t.module;
  221. orgname:=m.modulename^;
  222. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' Continues. State: ',m.state);{$ENDIF}
  223. if Assigned(t.state) then
  224. t.RestoreState;
  225. case m.state of
  226. ms_registered : parser.compile_module(m);
  227. ms_compile :
  228. begin
  229. if m=main then
  230. begin
  231. macrosymtablestack.clear;
  232. FreeAndNil(macrosymtablestack);
  233. end;
  234. parser.compile_module(m);
  235. end;
  236. ms_compiled : if (not m.is_initial) or m.is_unit then
  237. (m as tppumodule).post_load_or_compile(m,m.compilecount>1);
  238. ms_compiling_wait : pmodules.proc_program_declarations(m,m.islibrary);
  239. ms_compiling_waitintf : pmodules.parse_unit_interface_declarations(m);
  240. ms_compiling_waitimpl : pmodules.proc_unit_implementation(m);
  241. ms_compiling_waitfinish : pmodules.finish_compile_unit(m);
  242. ms_compiled_waitcrc : pmodules.finish_unit(m);
  243. ms_processed : ;
  244. else
  245. InternalError(2024011801);
  246. end;
  247. if m.state=ms_compiled then
  248. begin
  249. parsing_done(m);
  250. if m.is_initial and not m.is_unit then
  251. m.state:=ms_processed;
  252. end;
  253. Result:=m.state=ms_processed;
  254. {$IFDEF DEBUG_CTASK}
  255. Write(m.ToString,' done: ',Result);
  256. if Result then
  257. Writeln
  258. else
  259. Writeln(', state is now: ',m.state);
  260. {$ENDIF}
  261. if not result then
  262. // Not done, save state
  263. t.SaveState;
  264. {
  265. the name can change as a result of processing, e.g. PROGRAM -> TB0406
  266. Normally only for the initial module, but we'll do a generic check.
  267. }
  268. if m.modulename^<>orgname then
  269. rebuild_hash;
  270. end;
  271. procedure ttask_handler.rebuild_hash;
  272. var
  273. t : ttask_list;
  274. begin
  275. Hash.Clear;
  276. t:=list.firsttask;
  277. While assigned(t) do
  278. begin
  279. Hash.Add(t.module.modulename^,t);
  280. t:=t.nexttask;
  281. end;
  282. end;
  283. procedure ttask_handler.processqueue;
  284. var
  285. t,t2 : ttask_list;
  286. process: boolean;
  287. dummy,firstwaiting : tmodule;
  288. begin
  289. t:=list.firsttask;
  290. process:=true;
  291. While t<>nil do
  292. begin
  293. process:=cancontinue(t,firstwaiting);
  294. {$IFDEF Debug_WaitCRC}
  295. if firstwaiting<>nil then
  296. writeln('ttask_handler.processqueue "',t.module.realmodulename^,'" state=',t.module.state,' waitingfor="',firstwaiting.realmodulename^,'",',firstwaiting.state)
  297. else
  298. writeln('ttask_handler.processqueue "',t.module.realmodulename^,'" state=',t.module.state,' waitingfor=nil');
  299. {$ENDIF}
  300. if process then
  301. begin
  302. if continue(t) then
  303. begin
  304. {$IFDEF DEBUG_CTASK}Writeln(t.module.ToString,' is finished, removing from task list');{$ENDIF}
  305. hash.Remove(t.module);
  306. list.Remove(t);
  307. FreeAndNil(t);
  308. end;
  309. // first search for any module that is ready to be written as ppu
  310. t2:=list.firsttask;
  311. while (t2<>nil)
  312. and ((t2.module.state<>ms_compiled_waitcrc)
  313. or not t2.module.usedunitsfinalcrc(firstwaiting)) do
  314. t2:=t2.nexttask;
  315. if t2<>nil then
  316. begin
  317. t:=t2;
  318. {$IFDEF Debug_WaitCRC}
  319. writeln('ttask_handler.processqueue FOUND CRC READY ',t.module.realmodulename^,' state=',t.module.state);
  320. {$ENDIF}
  321. end
  322. else
  323. begin
  324. // maybe the strategy can be improved.
  325. t:=list.firsttask;
  326. end;
  327. end
  328. else if assigned(firstwaiting) and cancontinue(firstwaiting,true, dummy) then
  329. begin
  330. t2:=findtask(firstwaiting);
  331. if t2=nil then
  332. t2:=t.nexttask;
  333. t:=t2;
  334. end
  335. else
  336. begin
  337. t:=t.nexttask;
  338. end;
  339. if t=nil then
  340. begin
  341. t:=list.firsttask;
  342. if t<>nil then
  343. begin
  344. // no progress possible
  345. write_queue;
  346. InternalError(2025090301);
  347. end;
  348. end;
  349. end;
  350. end;
  351. procedure ttask_handler.addmodule(m: tmodule);
  352. var
  353. n : TSymStr;
  354. e, t : ttask_list;
  355. begin
  356. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' added to task scheduler. State: ',m.state);{$ENDIF}
  357. n:=m.modulename^;
  358. e:=ttask_list(Hash.Find(n));
  359. if e=nil then
  360. begin
  361. // Clear reset flag.
  362. // This can happen when during load, reset is done and unit is added to task list.
  363. m.is_reset:=false;
  364. t:=ttask_list.create(m);
  365. list.insert(t);
  366. hash.Add(n,t);
  367. if list.count=1 then
  368. main:=m;
  369. end
  370. else
  371. begin
  372. // We have a task, if it was reset, then clear the state and move the task to the start.
  373. if m.is_reset then
  374. begin
  375. {$IFDEF DEBUG_CTASK}Writeln(m.ToString,' was reset, resetting flag. State: ',m.state);{$ENDIF}
  376. m.is_reset:=false;
  377. t:=findtask(m);
  378. if assigned(t) then
  379. begin
  380. t.DiscardState;
  381. list.Remove(t);
  382. list.insertbefore(t,list.First);
  383. end;
  384. end;
  385. end;
  386. end;
  387. procedure ttask_handler.write_queue;
  388. var
  389. t: ttask_list;
  390. firstwaiting: tmodule;
  391. cc: Boolean;
  392. begin
  393. writeln('ttask_handler.write_queue:');
  394. t:=list.firsttask;
  395. while t<>nil do
  396. begin
  397. cc:=cancontinue(t,firstwaiting);
  398. if firstwaiting<>nil then
  399. writeln('queue: ',t.module.realmodulename^,' ',t.module.state,' cancontinue=',cc,' firstwaiting=',firstwaiting.realmodulename^,' ',firstwaiting.state)
  400. else
  401. writeln('queue: ',t.module.realmodulename^,' ',t.module.state,' cancontinue=',cc,' firstwaiting=nil');
  402. t:=t.nexttask;
  403. end;
  404. end;
  405. end.