Skip to content

Commit 6581863

Browse files
authored
Merge pull request #99 from BiffoBear/fix_out_of_sockets
Fix out of sockets problem
2 parents 88f5176 + d282022 commit 6581863

File tree

3 files changed

+117
-17
lines changed

3 files changed

+117
-17
lines changed

adafruit_wiznet5k/adafruit_wiznet5k.py

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@
4545

4646
from random import randint
4747
import time
48+
import gc
4849
from micropython import const
4950

5051
from adafruit_bus_device.spi_device import SPIDevice
5152
import adafruit_wiznet5k.adafruit_wiznet5k_dhcp as dhcp
5253
import adafruit_wiznet5k.adafruit_wiznet5k_dns as dns
54+
from adafruit_wiznet5k.adafruit_wiznet5k_debug import debug_msg
5355

5456
# Wiznet5k Registers
5557
_REG_MR = const(0x0000) # Mode
@@ -144,6 +146,8 @@ class WIZNET5K: # pylint: disable=too-many-public-methods, too-many-instance-at
144146
_UDP_MODE = const(0x02)
145147
_TLS_MODE = const(0x03) # This is NOT currently implemented
146148

149+
_sockets_reserved = []
150+
147151
# pylint: disable=too-many-arguments
148152
def __init__(
149153
self,
@@ -189,6 +193,13 @@ def __init__(
189193
self._ch_base_msb = 0
190194
if self._w5xxx_init() != 1:
191195
raise RuntimeError("Failed to initialize WIZnet module.")
196+
if self._chip_type == "w5100s":
197+
WIZNET5K._sockets_reserved = [False] * (_W5100_MAX_SOCK_NUM - 1)
198+
elif self._chip_type == "w5500":
199+
WIZNET5K._sockets_reserved = [False] * (_W5200_W5500_MAX_SOCK_NUM - 1)
200+
else:
201+
raise RuntimeError("Unrecognized chip type.")
202+
192203
# Set MAC address
193204
self.mac_address = mac
194205
self.src_port = 0
@@ -722,26 +733,61 @@ def _send_socket_cmd(self, socket: int, cmd: int) -> None:
722733
if self._debug:
723734
print("waiting for sncr to clear...")
724735

725-
def get_socket(self) -> int:
726-
"""Request, allocate and return a socket from the W5k chip.
736+
def get_socket(self, *, reserve_socket=False) -> int:
737+
"""
738+
Request, allocate and return a socket from the W5k chip.
739+
740+
Cycle through the sockets to find the first available one. If the called with
741+
reserve_socket=True, update the list of reserved sockets (intended to be used with
742+
socket.socket()). Note that reserved sockets must be released by calling
743+
cancel_reservation() once they are no longer needed.
744+
745+
If all sockets are reserved, no sockets are available for DNS calls, etc. Therefore,
746+
one socket cannot be reserved. Since socket 0 is the only socket that is capable of
747+
operating in MacRAW mode, it is the non-reservable socket.
748+
749+
:param bool reserve_socket: Whether to reserve the socket.
727750
728-
Cycle through the sockets to find the first available one, if any.
751+
:returns int: The first available socket.
729752
730-
:return int: The first available socket. Returns 0xFF if no sockets are free.
753+
:raises RuntimeError: If no socket is available.
731754
"""
732-
if self._debug:
733-
print("*** Get socket")
755+
debug_msg("*** Get socket.", self._debug)
756+
# Prefer socket zero for none reserved calls as it cannot be reserved.
757+
if not reserve_socket and self.socket_status(0)[0] == SNSR_SOCK_CLOSED:
758+
debug_msg("Allocated socket # 0", self._debug)
759+
return 0
760+
# Then check the other sockets.
734761

735-
sock = _SOCKET_INVALID
736-
for _sock in range(self.max_sockets):
737-
status = self.socket_status(_sock)[0]
738-
if status == SNSR_SOCK_CLOSED:
739-
sock = _sock
740-
break
762+
# Call garbage collection to encourage socket.__del__() be called to on any
763+
# destroyed instances. Not at all guaranteed to work!
764+
gc.collect()
765+
debug_msg(
766+
"Reserved sockets: {}".format(WIZNET5K._sockets_reserved), self._debug
767+
)
741768

742-
if self._debug:
743-
print("Allocated socket #{}".format(sock))
744-
return sock
769+
for socket_number, reserved in enumerate(WIZNET5K._sockets_reserved, start=1):
770+
if (
771+
not reserved
772+
and self.socket_status(socket_number)[0] == SNSR_SOCK_CLOSED
773+
):
774+
if reserve_socket:
775+
WIZNET5K._sockets_reserved[socket_number - 1] = True
776+
debug_msg(
777+
"Allocated socket # {}.".format(socket_number),
778+
self._debug,
779+
)
780+
return socket_number
781+
raise RuntimeError("Out of sockets.")
782+
783+
@staticmethod
784+
def release_socket(socket_number):
785+
"""
786+
Update the socket reservation list when a socket is no longer reserved.
787+
788+
:param int socket_number: The socket to release.
789+
"""
790+
WIZNET5K._sockets_reserved[socket_number - 1] = False
745791

746792
def socket_listen(
747793
self, socket_num: int, port: int, conn_mode: int = _SNMR_TCP
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# SPDX-FileCopyrightText: 2023 Martin Stephens
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""Makes a debug message function available to all modules."""
6+
try:
7+
from typing import TYPE_CHECKING, Union
8+
9+
if TYPE_CHECKING:
10+
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
11+
except ImportError:
12+
pass
13+
14+
import gc
15+
16+
17+
def debug_msg(
18+
message: Union[Exception, str, bytes, bytearray], debugging: bool
19+
) -> None:
20+
"""
21+
Helper function to print debugging messages. If the message is a bytes type
22+
object, create a hexdump.
23+
24+
:param Union[Exception, str, bytes, bytearray] message: The message to print.
25+
:param bool debugging: Only print if debugging is True.
26+
"""
27+
if debugging:
28+
if isinstance(message, (bytes, bytearray)):
29+
message = _hexdump(message)
30+
print(message)
31+
del message
32+
gc.collect()
33+
34+
35+
def _hexdump(src: bytes):
36+
"""
37+
Create a 16 column hexdump of a bytes object.
38+
39+
:param bytes src: The bytes object to hexdump.
40+
41+
:returns str: The hexdump.
42+
"""
43+
result = []
44+
for i in range(0, len(src), 16):
45+
chunk = src[i : i + 16]
46+
hexa = " ".join(("{:02x}".format(x) for x in chunk))
47+
text = "".join((chr(x) if 0x20 <= x < 0x7F else "." for x in chunk))
48+
result.append("{:04x} {:<48} {}".format(i, hexa, text))
49+
return "\n".join(result)

adafruit_wiznet5k/adafruit_wiznet5k_socket.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,18 @@ def __init__(
232232
self._timeout = _default_socket_timeout
233233
self._listen_port = None
234234

235-
self._socknum = _the_interface.get_socket()
235+
self._socknum = _the_interface.get_socket(reserve_socket=True)
236236
if self._socknum == _SOCKET_INVALID:
237237
raise RuntimeError("Failed to allocate socket.")
238238

239+
def __del__(self):
240+
_the_interface.release_socket(self._socknum)
241+
239242
def __enter__(self):
240243
return self
241244

242245
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
246+
_the_interface.release_socket(self._socknum)
243247
if self._sock_type == SOCK_STREAM:
244248
self._disconnect()
245249
stamp = time.monotonic()
@@ -625,8 +629,9 @@ def close(self) -> None:
625629
Mark the socket closed. Once that happens, all future operations on the socket object
626630
will fail. The remote end will receive no more data.
627631
"""
628-
self._socket_closed = True
632+
_the_interface.release_socket(self._socknum)
629633
_the_interface.socket_close(self._socknum)
634+
self._socket_closed = True
630635

631636
def _available(self) -> int:
632637
"""

0 commit comments

Comments
 (0)