11
11
package org .eclipse .rdf4j .sail .base ;
12
12
13
13
import java .lang .invoke .VarHandle ;
14
+ import java .lang .ref .Reference ;
14
15
import java .util .ArrayList ;
15
16
import java .util .Arrays ;
16
17
import java .util .Collection ;
23
24
import java .util .Objects ;
24
25
import java .util .Set ;
25
26
import java .util .concurrent .Semaphore ;
27
+ import java .util .concurrent .atomic .AtomicInteger ;
26
28
import java .util .concurrent .atomic .LongAdder ;
27
29
import java .util .concurrent .locks .StampedLock ;
28
30
import java .util .stream .Collectors ;
51
53
@ InternalUseOnly
52
54
public abstract class Changeset implements SailSink , ModelFactory {
53
55
56
+ static class CountedReference <T > {
57
+ final T referent ;
58
+ final AtomicInteger count = new AtomicInteger (1 );
59
+
60
+ CountedReference (T referent ) {
61
+ this .referent = referent ;
62
+ }
63
+
64
+ CountedReference <T > retain () {
65
+ count .incrementAndGet ();
66
+ return this ;
67
+ }
68
+
69
+ boolean release () {
70
+ return count .decrementAndGet () == 0 ;
71
+ }
72
+
73
+ T get () {
74
+ return referent ;
75
+ }
76
+ }
77
+
54
78
AdderBasedReadWriteLock readWriteLock = new AdderBasedReadWriteLock ();
55
79
AdderBasedReadWriteLock refBacksReadWriteLock = new AdderBasedReadWriteLock ();
56
80
Semaphore prependLock = new Semaphore (1 );
@@ -78,15 +102,15 @@ public abstract class Changeset implements SailSink, ModelFactory {
78
102
* <p>
79
103
* DO NOT EXPOSE THE MODEL OUTSIDE OF THIS CLASS BECAUSE IT IS NOT THREAD-SAFE
80
104
*/
81
- private volatile Model approved ;
105
+ private volatile CountedReference < Model > approved ;
82
106
private volatile boolean approvedEmpty = true ;
83
107
84
108
/**
85
109
* Explicit statements that have been removed as part of a transaction, but have not yet been committed.
86
110
* <p>
87
111
* DO NOT EXPOSE THE MODEL OUTSIDE OF THIS CLASS BECAUSE IT IS NOT THREAD-SAFE
88
112
*/
89
- private volatile Model deprecated ;
113
+ private volatile CountedReference < Model > deprecated ;
90
114
private volatile boolean deprecatedEmpty = true ;
91
115
92
116
/**
@@ -132,16 +156,16 @@ public void close() throws SailException {
132
156
addedNamespaces = null ;
133
157
removedPrefixes = null ;
134
158
try {
135
- if (approved instanceof AutoCloseable ) {
136
- ((AutoCloseable ) approved ).close ();
159
+ if (approved != null && approved . release () && approved . get () instanceof AutoCloseable ) {
160
+ ((AutoCloseable ) approved . get () ).close ();
137
161
}
138
162
} catch (Exception e ) {
139
163
throw new SailException (e );
140
164
} finally {
141
165
approved = null ;
142
- if (deprecated instanceof AutoCloseable ) {
166
+ if (deprecated != null && deprecated . release () && deprecated . get () instanceof AutoCloseable ) {
143
167
try {
144
- ((AutoCloseable ) deprecated ).close ();
168
+ ((AutoCloseable ) deprecated . get () ).close ();
145
169
} catch (Exception e ) {
146
170
throw new SailException (e );
147
171
} finally {
@@ -184,7 +208,7 @@ boolean hasApproved(Resource subj, IRI pred, Value obj, Resource[] contexts) {
184
208
185
209
boolean readLock = readWriteLock .readLock ();
186
210
try {
187
- return approved .contains (subj , pred , obj , contexts );
211
+ return approved .get (). contains (subj , pred , obj , contexts );
188
212
} finally {
189
213
readWriteLock .unlockReader (readLock );
190
214
}
@@ -206,7 +230,7 @@ boolean hasDeprecated(Resource subj, IRI pred, Value obj, Resource[] contexts) {
206
230
}
207
231
}
208
232
209
- return deprecated .contains (subj , pred , obj , contexts );
233
+ return deprecated .get (). contains (subj , pred , obj , contexts );
210
234
} finally {
211
235
readWriteLock .unlockReader (readLock );
212
236
}
@@ -389,7 +413,7 @@ public void clear(Resource... contexts) {
389
413
statementCleared = true ;
390
414
391
415
if (approved != null ) {
392
- approved .clear ();
416
+ approved .get (). clear ();
393
417
}
394
418
if (approvedContexts != null ) {
395
419
approvedContexts .clear ();
@@ -399,7 +423,7 @@ public void clear(Resource... contexts) {
399
423
deprecatedContexts = new HashSet <>();
400
424
}
401
425
if (approved != null ) {
402
- approved .remove (null , null , null , contexts );
426
+ approved .get (). remove (null , null , null , contexts );
403
427
}
404
428
if (approvedContexts != null && contexts != null ) {
405
429
for (Resource resource : contexts ) {
@@ -410,7 +434,7 @@ public void clear(Resource... contexts) {
410
434
deprecatedContexts .addAll (Arrays .asList (contexts ));
411
435
}
412
436
}
413
- approvedEmpty = approved == null || approved .isEmpty ();
437
+ approvedEmpty = approved == null || approved .get (). isEmpty ();
414
438
} finally {
415
439
readWriteLock .unlockWriter (writeLock );
416
440
}
@@ -425,13 +449,13 @@ public void approve(Statement statement) {
425
449
try {
426
450
427
451
if (deprecated != null ) {
428
- deprecated .remove (statement );
429
- deprecatedEmpty = deprecated == null || deprecated .isEmpty ();
452
+ deprecated .get (). remove (statement );
453
+ deprecatedEmpty = deprecated == null || deprecated .get (). isEmpty ();
430
454
}
431
455
if (approved == null ) {
432
- approved = createEmptyModel ();
456
+ approved = new CountedReference <>( createEmptyModel () );
433
457
}
434
- approved .add (statement );
458
+ approved .get (). add (statement );
435
459
approvedEmpty = false ;
436
460
if (statement .getContext () != null ) {
437
461
if (approvedContexts == null ) {
@@ -456,17 +480,17 @@ public void deprecate(Statement statement) {
456
480
long writeLock = readWriteLock .writeLock ();
457
481
try {
458
482
if (approved != null ) {
459
- approved .remove (statement );
460
- approvedEmpty = approved == null || approved .isEmpty ();
483
+ approved .get (). remove (statement );
484
+ approvedEmpty = approved == null || approved .get (). isEmpty ();
461
485
}
462
486
if (deprecated == null ) {
463
- deprecated = createEmptyModel ();
487
+ deprecated = new CountedReference <>( createEmptyModel () );
464
488
}
465
- deprecated .add (statement );
489
+ deprecated .get (). add (statement );
466
490
deprecatedEmpty = false ;
467
491
Resource ctx = statement .getContext ();
468
492
if (approvedContexts != null && approvedContexts .contains (ctx )
469
- && !approved .contains (null , null , null , ctx )) {
493
+ && !approved .get (). contains (null , null , null , ctx )) {
470
494
approvedContexts .remove (ctx );
471
495
}
472
496
} finally {
@@ -501,11 +525,11 @@ public String toString() {
501
525
sb .append (" deprecatedContexts, " );
502
526
}
503
527
if (deprecated != null ) {
504
- sb .append (deprecated .size ());
528
+ sb .append (deprecated .get (). size ());
505
529
sb .append (" deprecated, " );
506
530
}
507
531
if (approved != null ) {
508
- sb .append (approved .size ());
532
+ sb .append (approved .get (). size ());
509
533
sb .append (" approved, " );
510
534
}
511
535
if (sb .length () > 0 ) {
@@ -520,9 +544,9 @@ protected void setChangeset(Changeset from) {
520
544
assert !from .closed ;
521
545
522
546
this .observed = from .observed ;
523
- this .approved = from .approved ;
547
+ this .approved = from .approved != null ? from . approved . retain () : null ;
524
548
this .approvedEmpty = from .approvedEmpty ;
525
- this .deprecated = from .deprecated ;
549
+ this .deprecated = from .deprecated != null ? from . deprecated . retain () : null ;
526
550
this .deprecatedEmpty = from .deprecatedEmpty ;
527
551
this .approvedContexts = from .approvedContexts ;
528
552
this .deprecatedContexts = from .deprecatedContexts ;
@@ -689,7 +713,7 @@ List<Statement> getDeprecatedStatements() {
689
713
690
714
boolean readLock = readWriteLock .readLock ();
691
715
try {
692
- return new ArrayList <>(deprecated );
716
+ return new ArrayList <>(deprecated . get () );
693
717
} finally {
694
718
readWriteLock .unlockReader (readLock );
695
719
}
@@ -704,7 +728,7 @@ List<Statement> getApprovedStatements() {
704
728
705
729
boolean readLock = readWriteLock .readLock ();
706
730
try {
707
- return new ArrayList <>(approved );
731
+ return new ArrayList <>(approved . get () );
708
732
} finally {
709
733
readWriteLock .unlockReader (readLock );
710
734
}
@@ -725,7 +749,7 @@ boolean hasDeprecated(Statement statement) {
725
749
}
726
750
}
727
751
if (deprecated != null ) {
728
- return deprecated .contains (statement );
752
+ return deprecated .get (). contains (statement );
729
753
} else {
730
754
return false ;
731
755
}
@@ -751,7 +775,7 @@ Iterable<Statement> getApprovedStatements(Resource subj, IRI pred, Value obj,
751
775
boolean readLock = readWriteLock .readLock ();
752
776
try {
753
777
754
- Iterable <Statement > statements = approved .getStatements (subj , pred , obj , contexts );
778
+ Iterable <Statement > statements = approved .get (). getStatements (subj , pred , obj , contexts );
755
779
756
780
// This is a synchronized context, users of this method will be allowed to use the results at their leisure.
757
781
// We
@@ -788,7 +812,8 @@ Iterable<Triple> getApprovedTriples(Resource subj, IRI pred, Value obj) {
788
812
try {
789
813
// TODO none of this is particularly well thought-out in terms of performance, but we are aiming
790
814
// for functionally complete first.
791
- Stream <Triple > approvedSubjectTriples = approved .parallelStream ()
815
+ Stream <Triple > approvedSubjectTriples = approved .get ()
816
+ .parallelStream ()
792
817
.filter (st -> st .getSubject ().isTriple ())
793
818
.map (st -> (Triple ) st .getSubject ())
794
819
.filter (t -> {
@@ -801,7 +826,8 @@ Iterable<Triple> getApprovedTriples(Resource subj, IRI pred, Value obj) {
801
826
return obj == null || obj .equals (t .getObject ());
802
827
});
803
828
804
- Stream <Triple > approvedObjectTriples = approved .parallelStream ()
829
+ Stream <Triple > approvedObjectTriples = approved .get ()
830
+ .parallelStream ()
805
831
.filter (st -> st .getObject ().isTriple ())
806
832
.map (st -> (Triple ) st .getObject ())
807
833
.filter (t -> {
@@ -825,8 +851,8 @@ void removeApproved(Statement next) {
825
851
long writeLock = readWriteLock .writeLock ();
826
852
try {
827
853
if (approved != null ) {
828
- approved .remove (next );
829
- approvedEmpty = approved == null || approved .isEmpty ();
854
+ approved .get (). remove (next );
855
+ approvedEmpty = approved == null || approved .get (). isEmpty ();
830
856
}
831
857
} finally {
832
858
readWriteLock .unlockWriter (writeLock );
@@ -850,7 +876,7 @@ void sinkApproved(SailSink sink) {
850
876
boolean readLock = readWriteLock .readLock ();
851
877
try {
852
878
if (approved != null ) {
853
- sink .approveAll (approved , approvedContexts );
879
+ sink .approveAll (approved . get () , approvedContexts );
854
880
}
855
881
} finally {
856
882
readWriteLock .unlockReader (readLock );
@@ -865,7 +891,7 @@ void sinkDeprecated(SailSink sink) {
865
891
boolean readLock = readWriteLock .readLock ();
866
892
try {
867
893
if (deprecated != null ) {
868
- sink .deprecateAll (deprecated );
894
+ sink .deprecateAll (deprecated . get () );
869
895
}
870
896
} finally {
871
897
readWriteLock .unlockReader (readLock );
@@ -895,12 +921,12 @@ public void approveAll(Set<Statement> approve, Set<Resource> approveContexts) {
895
921
try {
896
922
897
923
if (deprecated != null ) {
898
- deprecated .removeAll (approve );
924
+ deprecated .get (). removeAll (approve );
899
925
}
900
926
if (approved == null ) {
901
- approved = createEmptyModel ();
927
+ approved = new CountedReference <>( createEmptyModel () );
902
928
}
903
- approved .addAll (approve );
929
+ approved .get (). addAll (approve );
904
930
approvedEmpty = approvedEmpty && approve .isEmpty ();
905
931
906
932
if (approveContexts != null ) {
@@ -921,19 +947,19 @@ public void deprecateAll(Set<Statement> deprecate) {
921
947
try {
922
948
923
949
if (approved != null ) {
924
- approved .removeAll (deprecate );
925
- approvedEmpty = approved == null || approved .isEmpty ();
950
+ approved .get (). removeAll (deprecate );
951
+ approvedEmpty = approved == null || approved .get (). isEmpty ();
926
952
}
927
953
if (deprecated == null ) {
928
- deprecated = createEmptyModel ();
954
+ deprecated = new CountedReference <>( createEmptyModel () );
929
955
}
930
- deprecated .addAll (deprecate );
956
+ deprecated .get (). addAll (deprecate );
931
957
deprecatedEmpty = deprecatedEmpty && deprecate .isEmpty ();
932
958
933
959
for (Statement statement : deprecate ) {
934
960
Resource ctx = statement .getContext ();
935
961
if (approvedContexts != null && approvedContexts .contains (ctx )
936
- && !approved .contains (null , null , null , ctx )) {
962
+ && !approved .get (). contains (null , null , null , ctx )) {
937
963
approvedContexts .remove (ctx );
938
964
}
939
965
}
0 commit comments