-
Notifications
You must be signed in to change notification settings - Fork 79
Add lazy initialization and isLookupEnabled #411
Add lazy initialization and isLookupEnabled #411
Conversation
It depends on #408 and therefore contains its commit messages as well. When the parent is merged, I will rebase. Please, check only the other two last commits. |
Failed tests are somehow related to The tests are expecting memory-related exceptions that don't get to be thrown but correctly (un)pickled. The following comment explains that there were some errors thrown by Jenkins: https://github.com/scala/pickling/blob/0.11.x/core/src/test/scala/pickling/run/share-json.scala#L64. Interestingly, all the tests pass if run using Intellij. |
No, actually these tests should fail, there's a circular dependency and |
My guess of what's happening: Intellij and sbt runs tests differently. Somehow, with sbt, the tests share some global state. Therefore, it is first executed and registered the (un)pickler for EDIT: My guess has been confirmed. Working on a fix for this. |
@@ -52,7 +52,15 @@ final class DefaultPicklerRegistry(generator: RuntimePicklerGenerator) extends P | |||
override def registerUnpickler[T](key: String, p: Unpickler[T]): Unit = | |||
unpicklerMap.put(key, p) | |||
|
|||
/** Checks the existince of an unpickler. */ | |||
/** Checks the existence of a pickler ignoring the registered generators. */ | |||
override def lookupExistingPickler(key: String): Option[Pickler[_]] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: Generators are for type constructors and SHOULD be safe to call. They do not involve runtime reflection.
E.g. List[_]
is impossible to register a pickler for, but you can register a pickler that handles it if it can lookup a pickler for element types.
Note: I may be adding statically generated "generator" functions for types like List[_]
where you leave some of the type constructor args "empty" and we get them passed in at runtime and do a runtime lookup (but not runtime reflection).
In any case, you shoudl be using lookupPickler/lookupUnpickler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I know they do not involve runtime reflection. But we want that a concrete pickler has preference over the generator. That is to say that Pickler[List[String]]
should be returned and registered instead of returning a generator Pickler[List[_]]
. That's why these methods exist: we want to check when we initialize concrete (un)picklers that there is no other concrete (un)pickler for that type. This was done with #410 on mind, thus those tests will pass.
NICE! Generally LGTM, but one caveat: Tests are failing. This is another instance where because we have a global runtime registry of picklers, you'll need to "swap" or "clear" the register in the test to make sure we can generate a new pickler with sharing disabled. It may make sense to add a "clear" method to to the pickler registry just for testing. You could probably make it Option #2 is to implement a thread-local pickler registry and swap threads. That seems pretty crazy and not a good idea.... |
I think there is even a better solution: to keep track of what picklers/unpicklers are generated with I'll add the WDYT? BTW, I'm working on this right now, i'll have it in 10 min or so. |
@jvican Well, considering I'm thinking of deprecating the way we handle sharing (because it's subtly broken for most statically generated picklers), I'm not sure we want to expose more sharing-related code :) That said, tracking in the pickler whether or not it supports sharing is probably best. That said, if you have one pickler with sharing and another without in the same runtime, isn't that a logic bug in your program? |
It depends... going again to the analogy of client-server, wouldn't be good to support the use case of having a client that fully knows the message that is sending (and therefore enables shareNothing) and a server (who doesn't know anything about the message received, but knows that it could be either a tree or graph, and therefore enables shareEverything)? Also, it's not necessary to keep track of that in the (un)picklers, we can do it just in the |
@jvican So the client/server analogy falls apart here. The issue only arises same JVM. The reasons tests fail is those tests do teh following (in sequence):
So for testing reasons, being able to "clean" the registered picklers between tests may be useful. Eitehr that Or when we test non-sharing we create totally new types/classees. That's probably the easiest (and least hacky) solution :) |
Your suggestion of creating totally new types for non-sharing (un)picklers is great. I think that it's very similar to my current solution (which I'm about to submit) but I don't embed that "sharing" information in the type, just in a dictionary. Tests are passing now in my machine. Otherwise, if you prefer to strictly follow the 'embed-in-the-types' strategy I can give it a try! |
* Add a field in the `PicklerRegistry` that check if the lookup is enabled for every kind of registr.
* Now, generated picklers/unpicklers are initialised only if there is no pickler/unpickler already registered for a given type. If that's the case, the registered on is reused. * There's a special handling for the generation of both Pickler and Unpickler. We have to make sure that both pickler/unpickler exist and that they are the same one, since we have to return a class that matches the return type (`Pickler[T]` with `Unpickler[T]`). * As a side note, it's necessary to cast `Unpickler`s and `Pickler with Unpickler` to `Generated`, otherwise the implicit search fails.
let me see what you have, if it's almost done, then I'll comment :) Hopefully it's good to go. |
* Because of lazy initialization, checks are not necessary anymore.
4147b37
to
07c28cf
Compare
private val unpicklerMap: Mmap[String, Unpickler[_]] = new TrieMap[String, Unpickler[_]] | ||
private val unpicklerGenMap: Mmap[String, UnpicklerGenerator] = new TrieMap[String, UnpicklerGenerator] | ||
|
||
private val shareNothingPicklers: Mset[String] = Mset.empty[String] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that most users will be picking ONE share strategy for their entire protocol, I do think this is a pretty big overhead/burden on the API/users for such a small use case (specifically the test).
@jvican SO, I think pickling strategies are already complicated enough. I think maybe we just fix the tests so the "share nothing" types are not the same as the 'share' tests... |
I agree, working on the |
f44ae1e
to
b1aa05b
Compare
Tests are passing with the |
@@ -98,5 +98,7 @@ trait PicklerRegistry { | |||
* this type. | |||
*/ | |||
def registerPicklerUnpicklerGenerator[T](typeConstructorKey: String, generator: FastTypeTag[_] => (Pickler[T] with Unpickler[T])): Unit | |||
// TODO - Some kind of clean or inspect what we have? | |||
|
|||
private[pickling] def clearRegisteredPicklerUnpicklerFor[T: FastTypeTag]: Unit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can you add some quick docs explaining this? We understand, but someone new to the codebase will be curious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally forgot 😄
One minor nit, fix that and feel free to merge! looking great. |
* Different generated picklers with opposite sharing strategies are created and only the first one is registered. The second one never gets initiliazed and reuses the first one. * Remove `freshName` since scala `2.10.0` doesn't support.
b1aa05b
to
0d03bcf
Compare
Tests passed. Merging this! |
pickler/unpickler already registered for a given type. If that's the
case, the registered on is reused.
Unpickler. We have to make sure that both pickler/unpickler exist and
that they are the same one, since we have to return a class that
matches the return type (
Pickler[T]
withUnpickler[T]
).Unpickler
s andPickler with Unpickler
toGenerated
, otherwise the implicit searchfails.
PicklerRegistry
that check if the lookup isenabled for every kind of registr.