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