Skip to content

Commit 4243f7c

Browse files
committed
Backport Lazy util
1 parent 80de816 commit 4243f7c

File tree

3 files changed

+330
-0
lines changed

3 files changed

+330
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.util;
18+
19+
import java.util.Objects;
20+
import java.util.function.Function;
21+
import java.util.function.Supplier;
22+
23+
/**
24+
* Provides a lazily-initialized value from a {@code Supplier<T>}.
25+
*
26+
* @param <T> type of value
27+
*/
28+
public interface Lazy<T> extends Supplier<T> {
29+
/**
30+
* Returns the value held by this lazy. This may cause the value to initialize if it hasn't been already.
31+
*/
32+
T value();
33+
34+
/**
35+
* Returns the value held by this lazy. This may cause the value to initialize if it hasn't been already.
36+
*/
37+
@Override
38+
default T get() {
39+
return value();
40+
}
41+
42+
/**
43+
* Creates a new lazy value derived from this lazy value using the provided value mapping function.
44+
*/
45+
default <R> Lazy<R> map(final Function<? super T, ? extends R> function) {
46+
return lazy(() -> function.apply(value()));
47+
}
48+
49+
/**
50+
* Indicates whether this lazy value has been initialized.
51+
*/
52+
boolean isInitialized();
53+
54+
/**
55+
* Sets this lazy value to the provided value.
56+
*
57+
* @throws UnsupportedOperationException if this type of lazy value cannot be updated
58+
*/
59+
void set(final T newValue);
60+
61+
/**
62+
* Creates a strict lazy value using the provided Supplier. The supplier is guaranteed to only be invoked by at
63+
* most one thread, and all threads will see the same published value when this returns.
64+
*/
65+
static <T> Lazy<T> lazy(final Supplier<T> supplier) {
66+
Objects.requireNonNull(supplier);
67+
return new LazyUtil.SafeLazy<>(supplier);
68+
}
69+
70+
/**
71+
* Creates a lazy value using the provided constant value.
72+
*/
73+
static <T> Lazy<T> value(final T value) {
74+
return new LazyUtil.Constant<>(value);
75+
}
76+
77+
/**
78+
* Creates a lazy value using a weak reference to the provided value.
79+
*/
80+
static <T> Lazy<T> weak(final T value) {
81+
return new LazyUtil.WeakConstant<>(value);
82+
}
83+
84+
/**
85+
* Creates a pure lazy value using the provided Supplier to initialize the value. The supplier may be invoked more
86+
* than once, and the return value should be a purely computed value as the result may be a different instance
87+
* each time. This is useful for building cache tables and other pure computations.
88+
*/
89+
static <T> Lazy<T> pure(final Supplier<T> supplier) {
90+
Objects.requireNonNull(supplier);
91+
return new LazyUtil.PureLazy<>(supplier);
92+
}
93+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.util;
18+
19+
import java.util.concurrent.locks.Lock;
20+
import java.util.concurrent.locks.ReentrantLock;
21+
import java.util.function.BooleanSupplier;
22+
23+
public class LazyBoolean implements BooleanSupplier {
24+
private final BooleanSupplier supplier;
25+
private final Lock lock = new ReentrantLock();
26+
private volatile boolean initialized;
27+
private volatile boolean value;
28+
29+
public LazyBoolean(final BooleanSupplier supplier) {
30+
this.supplier = supplier;
31+
}
32+
33+
@Override
34+
public boolean getAsBoolean() {
35+
boolean uninitialized = !initialized;
36+
boolean value = this.value;
37+
if (uninitialized) {
38+
lock.lock();
39+
try {
40+
uninitialized = !initialized;
41+
if (uninitialized) {
42+
this.value = value = supplier.getAsBoolean();
43+
initialized = true;
44+
}
45+
} finally {
46+
lock.unlock();
47+
}
48+
}
49+
return value;
50+
}
51+
52+
public void setAsBoolean(final boolean b) {
53+
lock.lock();
54+
try {
55+
initialized = false;
56+
value = b;
57+
initialized = true;
58+
} finally {
59+
lock.unlock();
60+
}
61+
}
62+
63+
public void reset() {
64+
initialized = false;
65+
}
66+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.util;
18+
19+
import java.lang.ref.WeakReference;
20+
import java.util.concurrent.locks.Lock;
21+
import java.util.concurrent.locks.ReentrantLock;
22+
import java.util.function.Supplier;
23+
24+
final class LazyUtil {
25+
private static final Object NULL = new Object() {
26+
@Override
27+
public String toString() {
28+
return "null";
29+
}
30+
};
31+
32+
static Object wrapNull(final Object value) {
33+
return value == null ? NULL : value;
34+
}
35+
36+
static <T> T unwrapNull(final Object value) {
37+
return value == NULL ? null : Cast.cast(value);
38+
}
39+
40+
static class Constant<T> implements Lazy<T> {
41+
private final T value;
42+
43+
Constant(final T value) {
44+
this.value = value;
45+
}
46+
47+
@Override
48+
public T value() {
49+
return value;
50+
}
51+
52+
@Override
53+
public boolean isInitialized() {
54+
return true;
55+
}
56+
57+
@Override
58+
public void set(final T newValue) {
59+
throw new UnsupportedOperationException();
60+
}
61+
62+
@Override
63+
public String toString() {
64+
return String.valueOf(value);
65+
}
66+
}
67+
68+
static class WeakConstant<T> implements Lazy<T> {
69+
private final WeakReference<T> reference;
70+
71+
WeakConstant(final T value) {
72+
reference = new WeakReference<>(value);
73+
}
74+
75+
@Override
76+
public T value() {
77+
return reference.get();
78+
}
79+
80+
@Override
81+
public boolean isInitialized() {
82+
return true;
83+
}
84+
85+
@Override
86+
public void set(final T newValue) {
87+
throw new UnsupportedOperationException();
88+
}
89+
90+
@Override
91+
public String toString() {
92+
return String.valueOf(value());
93+
}
94+
}
95+
96+
static class SafeLazy<T> implements Lazy<T> {
97+
private final Lock lock = new ReentrantLock();
98+
private final Supplier<T> supplier;
99+
private volatile Object value;
100+
101+
SafeLazy(final Supplier<T> supplier) {
102+
this.supplier = supplier;
103+
}
104+
105+
@Override
106+
public T value() {
107+
Object value = this.value;
108+
if (value == null) {
109+
lock.lock();
110+
try {
111+
value = this.value;
112+
if (value == null) {
113+
value = supplier.get();
114+
this.value = wrapNull(value);
115+
}
116+
} finally {
117+
lock.unlock();
118+
}
119+
}
120+
return unwrapNull(value);
121+
}
122+
123+
@Override
124+
public void set(final T newValue) {
125+
value = newValue;
126+
}
127+
128+
public void reset() {
129+
value = null;
130+
}
131+
132+
@Override
133+
public boolean isInitialized() {
134+
return value != null;
135+
}
136+
137+
@Override
138+
public String toString() {
139+
return isInitialized() ? String.valueOf(value) : "Lazy value not initialized";
140+
}
141+
}
142+
143+
static class PureLazy<T> implements Lazy<T> {
144+
private final Supplier<T> supplier;
145+
private Object value;
146+
147+
public PureLazy(final Supplier<T> supplier) {
148+
this.supplier = supplier;
149+
}
150+
151+
@Override
152+
public T value() {
153+
Object value = this.value;
154+
if (value == null) {
155+
value = supplier.get();
156+
this.value = wrapNull(value);
157+
}
158+
return unwrapNull(value);
159+
}
160+
161+
@Override
162+
public boolean isInitialized() {
163+
return value != null;
164+
}
165+
166+
@Override
167+
public void set(final T newValue) {
168+
value = newValue;
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)