1. class CompUnit::PrecompilationStore::File does CompUnit::PrecompilationStore {
  2. my class CompUnit::PrecompilationUnit::File does CompUnit::PrecompilationUnit {
  3. has CompUnit::PrecompilationId $.id;
  4. has IO::Path $.path;
  5. has IO::Handle $!file;
  6. has CompUnit::PrecompilationDependency @!dependencies;
  7. has $!initialized = False;
  8. has $.checksum;
  9. has $.source-checksum;
  10. has $!bytecode;
  11. has $!store;
  12. has Lock $!update-lock = Lock.new;
  13. submethod BUILD(
  14. CompUnit::PrecompilationId :$!id,
  15. IO::Path :$!path,
  16. :$!source-checksum,
  17. :@!dependencies,
  18. :$!bytecode,
  19. :$!store,
  20. --> Nil
  21. ) {
  22. if $!bytecode {
  23. $!initialized = True;
  24. $!checksum = nqp::sha1($!bytecode.decode("latin-1"));
  25. }
  26. }
  27. method !open() {
  28. $!file = $!path.open(:r);
  29. }
  30. method modified(--> Instant:D) {
  31. $!path.modified
  32. }
  33. method !read-dependencies() {
  34. $!update-lock.protect: {
  35. return if $!initialized;
  36. self!open(:r) unless $!file;
  37. $!checksum = $!file.get;
  38. $!source-checksum = $!file.get;
  39. my $dependency = $!file.get;
  40. while $dependency {
  41. @!dependencies.push: CompUnit::PrecompilationDependency::File.deserialize($dependency);
  42. $dependency = $!file.get;
  43. }
  44. $!initialized = True;
  45. }
  46. }
  47. method dependencies(--> Array[CompUnit::PrecompilationDependency]) {
  48. self!read-dependencies;
  49. @!dependencies
  50. }
  51. method bytecode(--> Buf:D) {
  52. $!update-lock.protect: {
  53. self!read-dependencies;
  54. $!bytecode //= $!file.slurp-rest(:bin,:close)
  55. }
  56. }
  57. method bytecode-handle(--> IO::Handle:D) {
  58. self!read-dependencies;
  59. $!file
  60. }
  61. method source-checksum() is rw {
  62. self!read-dependencies;
  63. $!source-checksum
  64. }
  65. method checksum() is rw {
  66. self!read-dependencies;
  67. $!checksum
  68. }
  69. method Str(--> Str:D) {
  70. self.path.Str
  71. }
  72. method close(--> Nil) {
  73. $!update-lock.protect: {
  74. $!file.close if $!file;
  75. $!file = Nil;
  76. }
  77. }
  78. method save-to(IO::Path $precomp-file) {
  79. my $handle = $precomp-file.open(:w);
  80. $handle.print($!checksum ~ "\n");
  81. $handle.print($!source-checksum ~ "\n");
  82. $handle.print($_.serialize ~ "\n") for @!dependencies;
  83. $handle.print("\n");
  84. $handle.write($!bytecode);
  85. $handle.close;
  86. $!path = $precomp-file;
  87. }
  88. method is-up-to-date(CompUnit::PrecompilationDependency $dependency, Bool :$check-source --> Bool) {
  89. my $result = self.CompUnit::PrecompilationUnit::is-up-to-date($dependency, :$check-source);
  90. $!store.remove-from-cache($.id) unless $result;
  91. $result
  92. }
  93. }
  94. has IO::Path $.prefix is required;
  95. has IO::Handle $!lock;
  96. has int $!lock-count = 0;
  97. has %!loaded;
  98. has %!compiler-cache;
  99. has %!dir-cache;
  100. has Lock $!update-lock = Lock.new;
  101. submethod BUILD(IO::Path :$!prefix --> Nil) {
  102. }
  103. method new-unit(|c) {
  104. CompUnit::PrecompilationUnit::File.new(|c, :store(self))
  105. }
  106. method !dir(CompUnit::PrecompilationId $compiler-id,
  107. CompUnit::PrecompilationId $precomp-id)
  108. {
  109. $!update-lock.protect: {
  110. %!dir-cache{$compiler-id ~ $precomp-id} //=
  111. (%!compiler-cache{$compiler-id} //= self.prefix.add($compiler-id.IO))
  112. .add($precomp-id.substr(0, 2).IO)
  113. }
  114. }
  115. method path(CompUnit::PrecompilationId $compiler-id,
  116. CompUnit::PrecompilationId $precomp-id,
  117. Str :$extension = '')
  118. {
  119. self!dir($compiler-id, $precomp-id).add(($precomp-id ~ $extension).IO)
  120. }
  121. method !lock(--> Nil) {
  122. return if $*W && $*W.is_precompilation_mode();
  123. my int $acquire-file-lock = $!update-lock.protect: {
  124. $!lock //= $.prefix.add('.lock').open(:create, :rw);
  125. $!lock-count++
  126. }
  127. $!lock.lock if $acquire-file-lock == 0;
  128. }
  129. method unlock() {
  130. return if $*W && $*W.is_precompilation_mode();
  131. $!update-lock.protect: {
  132. die "unlock when we're not locked!" if $!lock-count == 0;
  133. $!lock-count-- if $!lock-count > 0;
  134. $!lock && $!lock-count == 0 ?? $!lock.unlock !! True
  135. }
  136. }
  137. method load-unit(CompUnit::PrecompilationId $compiler-id,
  138. CompUnit::PrecompilationId $precomp-id)
  139. {
  140. $!update-lock.protect: {
  141. %!loaded{$precomp-id} //= do {
  142. my $path = self.path($compiler-id, $precomp-id);
  143. $path ~~ :e
  144. ?? CompUnit::PrecompilationUnit::File.new(:id($precomp-id), :$path, :store(self))
  145. !! Nil
  146. }
  147. }
  148. }
  149. method load-repo-id(CompUnit::PrecompilationId $compiler-id,
  150. CompUnit::PrecompilationId $precomp-id)
  151. {
  152. my $path = self.path($compiler-id, $precomp-id, :extension<.repo-id>);
  153. if $path ~~ :e {
  154. $path.slurp
  155. }
  156. else {
  157. Nil
  158. }
  159. }
  160. method remove-from-cache(CompUnit::PrecompilationId $precomp-id) {
  161. $!update-lock.protect: { %!loaded{$precomp-id}:delete };
  162. }
  163. method destination(CompUnit::PrecompilationId $compiler-id,
  164. CompUnit::PrecompilationId $precomp-id,
  165. Str :$extension = ''
  166. --> IO::Path:D)
  167. {
  168. unless $!prefix.e {
  169. $!prefix.mkdir or return;
  170. }
  171. return unless $!prefix.w;
  172. self!lock();
  173. self!file($compiler-id, $precomp-id, :$extension);
  174. }
  175. method !file(CompUnit::PrecompilationId $compiler-id,
  176. CompUnit::PrecompilationId $precomp-id,
  177. Str :$extension = ''
  178. --> IO::Path:D)
  179. {
  180. my $compiler-dir = self.prefix.add($compiler-id.IO);
  181. $compiler-dir.mkdir unless $compiler-dir.e;
  182. my $dest = self!dir($compiler-id, $precomp-id);
  183. $dest.mkdir unless $dest.e;
  184. $dest.add(($precomp-id ~ $extension).IO)
  185. }
  186. method store-file(CompUnit::PrecompilationId $compiler-id,
  187. CompUnit::PrecompilationId $precomp-id,
  188. IO::Path:D $path,
  189. :$extension = '')
  190. {
  191. $path.rename(self!file($compiler-id, $precomp-id, :$extension));
  192. }
  193. method store-unit(CompUnit::PrecompilationId $compiler-id,
  194. CompUnit::PrecompilationId $precomp-id,
  195. CompUnit::PrecompilationUnit $unit)
  196. {
  197. my $precomp-file = self!file($compiler-id, $precomp-id, :extension<.tmp>);
  198. $unit.save-to($precomp-file);
  199. $precomp-file.rename(self!file($compiler-id, $precomp-id));
  200. self.remove-from-cache($precomp-id);
  201. }
  202. method store-repo-id(CompUnit::PrecompilationId $compiler-id,
  203. CompUnit::PrecompilationId $precomp-id,
  204. :$repo-id!)
  205. {
  206. try self!file($compiler-id, $precomp-id, :extension<.repo-id>).spurt($repo-id);
  207. }
  208. method delete(
  209. CompUnit::PrecompilationId $compiler-id,
  210. CompUnit::PrecompilationId $precomp-id,
  211. Str :$extension = '')
  212. {
  213. self.path($compiler-id, $precomp-id, :$extension).unlink;
  214. }
  215. method delete-by-compiler(CompUnit::PrecompilationId $compiler-id)
  216. {
  217. my $compiler-dir = self.prefix.add($compiler-id.IO);
  218. for $compiler-dir.dir -> $subdir {
  219. $subdir.dir>>.unlink;
  220. $subdir.rmdir;
  221. }
  222. $compiler-dir.rmdir;
  223. }
  224. }