[ 
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)

Reply via email to