[
https://issues.apache.org/jira/browse/GROOVY-12674?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18137460#comment-18137460
]
ASF GitHub Bot commented on GROOVY-12674:
-----------------------------------------
codeconsole opened a new pull request, #3195:
URL: https://github.com/apache/groovy/pull/3195
`Closure` is `Serializable`, and a `memoize()` closure whose wrapped closure
has no non-serializable owner serializes. The other memoize variants never did:
```groovy
def base = { x -> x * 2 }.dehydrate()
roundTrip(base.memoize()) // works
roundTrip(base.memoizeAtMost(2)) // NotSerializableException: LRUCache
roundTrip(base.memoizeAtLeast(0)) // NotSerializableException:
NullProtectionStorage
roundTrip(base.memoizeAtLeast(2)) // NotSerializableException:
ReferenceQueue (SoftReference once called)
```
Seen in 4.0.33, 5.0.8 and 6.0.0 too.
`LRUCache` is now `Serializable`. Its `ConcurrentLinkedHashMap` already
serializes its capacity and entries, so `memoizeAtMost` keeps its cached values
and maximum size, like `memoize()` does.
The soft reference variants keep their protected cache size and rebuild the
protection storage and reference queue, now transient, when deserialized. Their
cached values are soft references, which cannot be serialized and may be
cleared by the garbage collector at any time; they now serialize as cleared
references, which the deserialized closure removes with
`cleanUpNullReferences`. So `memoizeAtLeast` and `memoizeBetween` are
deserialized with an empty cache with the same limits. This works for any
serializable `MemoizeCache` passed to
`Memoize.buildSoftReferenceMemoizeFunction`. No public signature changes.
Merging with #3190 (GROOVY-12669): `Closure.java` (Javadoc),
`AbstractMemoizeTestCase` (appended tests) and
`Memoize.SoftReferenceMemoizeFunction` conflict textually. Keep #3190's
constructors and `cacheFactory`, make `lruProtectionStorage` and `queue`
transient and non-final as here, have #3190's public constructor initialise
them through `initTransientFields()`, and keep this change's `readObject` and
reference classes. With both in, a memoized closure that is dehydrated
afterwards also serializes.
## Tests
`testSerializedCopyStillMemoizes` and
`testSerializedCopyOfDehydratedClosure` in `AbstractMemoizeTestCase` (all four
variants),
`MemoizeAtMostTest.testSerializedCopyKeepsCachedValuesAndMaxCacheSize`,
`MemoizeAtLeastTest.testSerializedCopyDropsCachedValues` and
`testSerializedCopyKeepsProtectedCacheSize`,
`MemoizeBetweenTest.testSerializedCopyDropsCachedValuesAndKeepsLimits`, and
`ClosureSerializationCycleTest.testLegitimateSoftReferenceMemoizedClosureRoundTrips`
fail without the change.
`ClosureSerializationCycleTest.serializeCyclicSoftMemoized` no longer has to
clear the unserializable fields to build its gadget stream. The
`org.codehaus.groovy.runtime.memoize`, `groovy.*Closure*` and
`groovy.*Memoize*` tests and `MemoizedASTTransformationTest` pass. The
Checkstyle and CodeNarc gates pass.
> memoizeAtMost, memoizeAtLeast and memoizeBetween closures cannot be
> serialized, unlike memoize closures
> -------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12674
> URL: https://issues.apache.org/jira/browse/GROOVY-12674
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 4.0.33, 5.0.8, 7.0.0-alpha-1, 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> {code:groovy}
> def roundTrip(Closure c) {
> def bytes = new ByteArrayOutputStream()
> bytes.withObjectOutputStream { it.writeObject(c) }
> new
> ByteArrayInputStream(bytes.toByteArray()).withObjectInputStream(getClass().classLoader)
> { it.readObject() }
> }
> def base = { x -> x * 2 }.dehydrate()
> assert roundTrip(base.memoize())(4) == 8 // works
> roundTrip(base.memoizeAtMost(2)) // NotSerializableException:
> org.codehaus.groovy.runtime.memoize.LRUCache
> roundTrip(base.memoizeAtLeast(0)) // NotSerializableException:
> org.codehaus.groovy.runtime.memoize.NullProtectionStorage
> roundTrip(base.memoizeAtLeast(2)) // NotSerializableException:
> java.lang.ref.ReferenceQueue
> def between = base.memoizeBetween(1, 3)
> between(1)
> roundTrip(between) // NotSerializableException:
> java.lang.ref.SoftReference
> {code}
> *Actual:* every variant except memoize() throws NotSerializableException,
> whether or not it has been called.
> *Expected:* they serialize like memoize() does and still memoize after
> deserialization, with the same limits.
> Cause: LRUCache is not Serializable. The soft reference variants hold a
> ReferenceQueue, a NullProtectionStorage or LRUProtectionStorage, and
> SoftReference cache values, none of which can be serialized.
> Verified on 4.0.33, 5.0.8, 6.0.0 and master.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)