Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PROPOSAL] thread_idx "lock" for communications #9

Open
wants to merge 1 commit into
base: intent_log
Choose a base branch
from

Conversation

bkj
Copy link

@bkj bkj commented Feb 14, 2023

This PR implemented the thread_idx "lock" I described on the call.

An example of the client-side code to support this functionality is

def filter_thread(msg):
    return (
        (msg.sender == own_power and msg.recipient == dst_power) or 
        (msg.sender == dst_power and msg.recipient == own_power)
    )

response = 0
while response == 0:
    # get thread (comms between me and dst_power)
    thread     = list(filter(filter_thread, game.messages.values()))

    # compute expected position in the thread
    thread_idx = len(thread)
    
    # generate a message based on the thread
    message    = create_message(thread)

    # try to send a message
    # - if the message is accepted, `response` is the timestamp it was it accepted
    # - if the message was bounced because `thread_idx` is incorrect, `response == 0`
    response = await game.send_game_message(dst_power, message, thread_idx=thread_idx) 
    
    if response == 0:
        await asyncio.sleep(1)   # wait a bit
        await game.synchronize() # update messages

E.g., if your thread_idx is wrong, it's because another message that you haven't seen yet was received by the server before your message. Thus, you may want to re-compute the message.

To ignore this functionality - and never have a message bounced - you can just do the normal:

await game.send_game_message(dst_power, message) 

To motivate the utility of this function - imagine PRP(A) and PRP(B) are mutually exclusive:

  • AUS sends PRP(A) and FRA sends PRP(B) at ~ the same time, without knowing about the other PRP.
  • Then, AUS sends YES(B) and FRA sends YES(A) at ~ the same time, without knowing about the other YES.
    What happens then? The negotiation is in a weird state. If this happened to two people, they might realize the conflict and pause to clarify ... but the bots might not be sophisticated to handle this situation at this point.

A small test shows these kinds of race conditions are actually pretty common -- in a single round of communication, this code catches three race conditions:

accepted server_type [1676054924290639/S1901M/TURKEY->ENGLAND/tid=0](PEACE(TURKEY ENGLAND))
accepted server_type [1676054924314901/S1901M/FRANCE->ENGLAND/tid=0](PEACE(FRANCE ENGLAND))
accepted server_type [1676054924322082/S1901M/TURKEY->RUSSIA/tid=0](PEACE(TURKEY RUSSIA))
accepted server_type [1676054924333371/S1901M/AUSTRIA->ITALY/tid=0](PEACE(AUSTRIA ITALY))
accepted server_type [1676054924335268/S1901M/ENGLAND->RUSSIA/tid=0](PEACE(ENGLAND RUSSIA))
* rejected server_type [1676054924337120/S1901M/RUSSIA->ENGLAND/tid=0](PEACE(RUSSIA ENGLAND))
accepted server_type [1676054924340226/S1901M/ITALY->ENGLAND/tid=0](PEACE(ITALY ENGLAND))
accepted server_type [1676054924342084/S1901M/GERMANY->FRANCE/tid=0](PEACE(GERMANY FRANCE))
accepted server_type [1676054924344416/S1901M/FRANCE->RUSSIA/tid=0](PEACE(FRANCE RUSSIA))
accepted server_type [1676054924362564/S1901M/TURKEY->ITALY/tid=0](PEACE(TURKEY ITALY))
* rejected server_type [1676054924368421/S1901M/ENGLAND->ITALY/tid=0](PEACE(ENGLAND ITALY))
accepted server_type [1676054924372534/S1901M/AUSTRIA->GERMANY/tid=0](PEACE(AUSTRIA GERMANY))
accepted server_type [1676054924382096/S1901M/ITALY->RUSSIA/tid=0](PEACE(ITALY RUSSIA))
* rejected server_type [1676054924384112/S1901M/GERMANY->AUSTRIA/tid=0](PEACE(GERMANY AUSTRIA))
accepted server_type [1676054924387729/S1901M/FRANCE->AUSTRIA/tid=0](PEACE(FRANCE AUSTRIA))
accepted server_type [1676054924392022/S1901M/FRANCE->TURKEY/tid=0](PEACE(FRANCE TURKEY))
accepted server_type [1676054924406874/S1901M/TURKEY->AUSTRIA/tid=0](PEACE(TURKEY AUSTRIA))
accepted server_type [1676054924420093/S1901M/AUSTRIA->ENGLAND/tid=0](PEACE(AUSTRIA ENGLAND))

@@ -893,7 +893,7 @@ def new_power_message(self, recipient, body):
assert self.is_player_game()
if not self.has_power(recipient):
raise exceptions.MapPowerException(recipient)
return Message(phase=self.current_short_phase, sender=self.role, recipient=recipient, message=body)
return Message(phase=self.current_short_phase, sender=self.role, recipient=recipient, message=body, thread_idx=thread_idx)
Copy link

@mjspeck mjspeck Feb 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bkj could you add the thread_idx param to the docstring? Otherwise, the only explanation for it would be in this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants