Skip to content

Commit

Permalink
Implement continuation cloning and SuspendCoroutine::suspendMultiple
Browse files Browse the repository at this point in the history
Summary:
This diff implements a rudimentary attempt at multi-shot continuations. You can read the design doc for the nitty-gritty. The high-level of this diff is the following:

  # Continuations are now represented by the interface `CloneableCoroutineContinuation` and provide a `clone` method. This method does a shallow-copy (i.e., copy-on-write) of the continuation's state (contained in the closure). Additionally, it does a `clone` on the next continuation in the chain.
  # The coroutines codegen now creates and passes around instances of `CloneableCoroutineContinuation`.
However, the entry and exit points from the codegen only deal in `CoroutineContinuation`, so we don't expose cloning to the end user. (See remaining bullet points.)
  # `CreateCoroutine::createWithReceiverUnchecked` still receives a `CoroutineContinuation` as the completion callback. We now wrap the completion callback in a `NonCloningCoroutineContinuation`, which causes it to just return itself when someone tries to clone it.
  # `SuspendCoroutine::suspendMultiple` is a new suspend primitive for coroutines. It uses `CloneOnResumeContinuation` to allow multiple resumptions of a continuation, each of which are cloned from an original continuation.

**Design doc:** https://fburl.com/multi-shot-continuations

(IMPORTANT) **Big note/warning:** There are two things that we still need to do. (1) Copy-in the state before every suspension. I'll do this in the next diff. (2) Right now, when multi-suspending, we **recurse** into the resumed continuation. This is bad, because it may create deep call stacks, and we might stack overflow. I'll write up a design to fix this, but it will be fairly complex. This problem is isolated to the new `suspendMultiple` method, so existing code is unaffected.

 ---

Reviewed By: ericlippert, vladima

Differential Revision: D5790893

fbshipit-source-id: 510e97972927f21fff24c4d09bac2064df3ca7c9
  • Loading branch information
michaeltingley authored and hhvm-bot committed Sep 19, 2017
1 parent decf3f7 commit ab34a88
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 15 deletions.
91 changes: 82 additions & 9 deletions hphp/hack/src/parser/coroutine/coroutine_closure_generator.ml
Expand Up @@ -39,24 +39,25 @@ let generate_constructor_method
state_machine_data =
let function_parameter_list =
make_parameters_public_and_untyped state_machine_data in
let cont_param =
make_continuation_parameter_syntax
~visibility_syntax:private_syntax
function_type in
let cont_param = make_continuation_parameter_syntax function_type in
let sm_param = make_state_machine_parameter_syntax context function_type in
let function_parameter_list =
cont_param :: sm_param :: function_parameter_list in
let ctor = make_constructor_decl_header_syntax
constructor_member_name function_parameter_list in
let call_parent_syntax =
make_construct_parent_syntax [ continuation_variable_syntax ] in
make_methodish_declaration_syntax ctor [ call_parent_syntax; ]
make_methodish_declaration_syntax
~modifiers:[ public_syntax; final_syntax; ]
ctor
[ call_parent_syntax; ]

let select_state_machine_syntax =
make_member_selection_expression_syntax
this_syntax
state_machine_member_name_syntax

let do_resume_body =
let select_state_machine_syntax =
make_member_selection_expression_syntax
this_syntax
state_machine_member_name_syntax in
let assign_state_machine_syntax =
make_assignment_syntax
state_machine_variable_name
Expand All @@ -82,6 +83,77 @@ let generate_do_resume_method function_type =
(make_coroutine_result_type_syntax function_type))
do_resume_body

let generate_clone_body { CoroutineStateMachineData.parameters; properties; } =
(* $this->coroutineContinuation_generated *)
let select_continuation_syntax =
make_member_selection_expression_syntax
this_syntax
continuation_member_name_syntax in
(* $this->coroutineContinuation_generated->clone *)
let select_continuation_clone_syntax =
make_member_selection_expression_syntax
select_continuation_syntax
clone_member_name_syntax in
let clone_continuation_syntax =
make_function_call_expression_syntax select_continuation_clone_syntax [] in
(* [ $this->arg1; $this->arg2; ... ] *)
let arg_list =
let get_parameter_as_member_variable { parameter_name; _; } =
parameter_name
|> string_of_variable_token
|> fun var -> String_utils.lstrip var "$"
|> make_name_syntax
|> make_member_selection_expression_syntax this_syntax in
Core_list.map ~f:get_parameter_as_member_variable parameters in
(* [
* $this->coroutineContinuation_generated->clone();
* $this->stateMachineFunction;
* $this->arg1;
* $this->arg2;
* ...
* ]
*)
let parameters =
clone_continuation_syntax ::
select_state_machine_syntax ::
arg_list in
(* new static(args) *)
let new_closure_syntax =
make_typed_object_creation_expression_syntax static_syntax parameters in
(* $closure = new static(args); *)
let closure_assignment_syntax =
make_assignment_syntax closure_variable new_closure_syntax in
(* [ $closure->coroutineResultData1 = $this->coroutineResultData1; ... ] *)
let copy_properties_syntaxes =
let make_copy_property_syntax property_name_string =
let property_member_name =
property_name_string
|> fun var -> String_utils.lstrip var "$"
|> make_name_syntax in
let this_member_syntax =
make_member_selection_expression_syntax
this_syntax
property_member_name in
let closure_member_syntax =
make_member_selection_expression_syntax
closure_variable_syntax
property_member_name in
make_assignment_syntax_variable
closure_member_syntax
this_member_syntax in
Core_list.map ~f:make_copy_property_syntax (next_label :: properties) in
(* return $closure; *)
let return_statement_syntax =
make_return_statement_syntax closure_variable_syntax in
closure_assignment_syntax ::
copy_properties_syntaxes @
[ return_statement_syntax; ]

let generate_clone_method state_machine_data =
make_methodish_declaration_syntax
(make_function_decl_header_syntax clone_member_name [] this_type_syntax)
(generate_clone_body state_machine_data)

let generate_closure_body
context
function_type
Expand All @@ -90,6 +162,7 @@ let generate_closure_body
@ [
generate_constructor_method context function_type state_machine_data;
generate_do_resume_method function_type;
generate_clone_method state_machine_data;
]

(**
Expand Down
44 changes: 38 additions & 6 deletions hphp/hack/src/parser/coroutine/coroutine_syntax.ml
Expand Up @@ -133,6 +133,9 @@ let private_syntax =
let public_syntax =
make_token_syntax TokenKind.Public

let final_syntax =
make_token_syntax TokenKind.Final

let static_syntax =
make_token_syntax TokenKind.Static

Expand Down Expand Up @@ -196,6 +199,10 @@ let member_selection_syntax =
let this_syntax =
make_variable_expression_syntax "$this"

let this_type_syntax =
make_name_syntax "this"
|> make_simple_type_specifier

let unset_syntax =
make_name_syntax "unset"

Expand Down Expand Up @@ -280,6 +287,10 @@ let string_of_name_token node =
| Token ({ Token.kind = TokenKind.Name; _; } as token) -> Token.text token
| _ -> failwith "string_of_name_token: Was not a Name Token"

let string_of_variable_token node =
match syntax node with
| Token ({ Token.kind = TokenKind.Variable; _; } as token) -> Token.text token
| _ -> failwith "string_of_variable_token: Was not a Variable Token"

(* Syntax creation functions *)

Expand Down Expand Up @@ -456,8 +467,9 @@ let make_type_specifier_syntax classname type_parameter_list =
else
make_generic_type_specifier classname_syntax type_arguments_syntax

let make_object_creation_expression_syntax classname arguments =
let type_specifier_syntax = make_type_specifier_syntax classname [] in
let make_typed_object_creation_expression_syntax
type_specifier_syntax
arguments =
let arguments_syntax = make_comma_list arguments in
make_object_creation_expression
new_keyword_syntax
Expand All @@ -466,6 +478,10 @@ let make_object_creation_expression_syntax classname arguments =
arguments_syntax
right_paren_syntax

let make_object_creation_expression_syntax classname arguments =
let type_specifier_syntax = make_type_specifier_syntax classname [] in
make_typed_object_creation_expression_syntax type_specifier_syntax arguments

let make_functional_type_syntax argument_types return_type_syntax =
let argument_types_syntax = make_comma_list argument_types in
make_closure_type_specifier
Expand Down Expand Up @@ -508,13 +524,13 @@ let make_classish_declaration_syntax
(* classish_implements_list *) (make_missing ())
classish_body

(* TODO(tingley): Determine if it's worth tightening visibility here. *)
let make_methodish_declaration_with_body_syntax
?(modifiers = [ public_syntax; ])
function_decl_header_syntax
function_body =
make_methodish_declaration
(* methodish_attribute *) (make_missing ())
(make_list [public_syntax])
(make_list modifiers)
function_decl_header_syntax
function_body
(* methodish_semicolon *) (make_missing ())
Expand Down Expand Up @@ -546,9 +562,11 @@ let make_lambda_from_method_syntax existing_node lambda_signature lambda_body =
})

let make_methodish_declaration_syntax
?modifiers
function_decl_header_syntax
function_body =
make_methodish_declaration_with_body_syntax
?modifiers
function_decl_header_syntax
(make_compound_statement_syntax function_body)

Expand Down Expand Up @@ -651,8 +669,14 @@ let default_label_syntax =

(* Coroutine-specific syntaxes *)

let continuation_member_name =
"coroutineContinuation_generated"

let continuation_member_name_syntax =
make_name_syntax continuation_member_name

let continuation_variable =
"$coroutineContinuation_generated"
"$" ^ continuation_member_name

let continuation_variable_syntax =
make_variable_expression_syntax continuation_variable
Expand All @@ -661,7 +685,9 @@ let make_closure_base_type_syntax function_type =
make_type_specifier_syntax "ClosureBase" [ function_type; ]

let make_continuation_type_syntax function_type =
make_type_specifier_syntax "CoroutineContinuation" [ function_type; ]
make_type_specifier_syntax
"InternalCoroutineContinuation"
[ function_type; ]

let make_continuation_parameter_syntax
?visibility_syntax
Expand Down Expand Up @@ -777,6 +803,12 @@ let do_resume_member_name =
let do_resume_member_name_syntax =
make_name_syntax do_resume_member_name

let clone_member_name =
"clone"

let clone_member_name_syntax =
make_name_syntax clone_member_name

let coroutine_data_variable =
"$coroutineData"

Expand Down

0 comments on commit ab34a88

Please sign in to comment.