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

Add full'ish support for network socket get & set options #2513

Merged
merged 20 commits into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
25e8060
Add functions to the Pony runtime for setsockopt(2) and getsockopt(2).
slfritchie Jan 19, 2018
4373db7
Shape of the new getsockopt & setsockopt API, it works!
slfritchie Feb 7, 2018
d3fa327
Split get/setsockopt and get/setsockopt_u32. Add docstrings.
slfritchie Feb 8, 2018
0171d59
WIP: WHA? NODELAY word size = 1, really?
slfritchie Feb 8, 2018
3be15d9
WIP: OK, NODELAY word size = 4, hooray
slfritchie Feb 8, 2018
471ba58
Add 'bigendian' and 'littleendian' to platformfuns et al.
slfritchie Feb 8, 2018
6542d32
Add 'ifdef littleendian' checks where byte twiddling is needed
slfritchie Feb 8, 2018
d444e3f
Clean up get/setsockopt stuff in under_pressure; use larger write size
slfritchie Feb 8, 2018
fec3577
Remove bad use of 'addressof' in last arg to pony_os_setsockopt: OS X…
slfritchie Feb 9, 2018
003fc06
Review: use llvm::Triple::isLittleEndian() method
slfritchie Feb 9, 2018
c177417
Review: semi-mechanically add _endian_flags table & management code t…
slfritchie Feb 9, 2018
b2d7443
Review: semi-mechanically add endian items got Platform and genprim.c
slfritchie Feb 9, 2018
7235c7b
Review: add missing ref cap to get/setsockopt functions in tcp_socket…
slfritchie Feb 14, 2018
a0110c9
Add usage examples for TCP & UDP get/setsockopt functions
slfritchie Feb 28, 2018
c8f3675
Expand & fix get/setsockopt docstrings
slfritchie Feb 28, 2018
95bb8cb
Fix get/setsockopt docstrings
slfritchie Feb 28, 2018
aa2c4ad
Fix get/setsockopt docstrings
slfritchie Mar 1, 2018
219d4c7
Remove the api_deprecated() function introduced earlier in this PR
slfritchie Mar 2, 2018
3602a9f
Change interface of getsockopt() per Dipin's suggestion
slfritchie Mar 2, 2018
8b807e5
Last review cleanup items, thanks Dipin
slfritchie Mar 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion examples/under_pressure/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ class SlowDown is TCPConnectionNotify
_auth = auth
_out = out

fun ref connected(conn: TCPConnection ref) =>
let bufsiz: U32 = 5000

@printf[I32]("Querying and setting socket options:\n".cstring())
@printf[I32]("\tgetsockopt so_error = %d\n".cstring(),
conn.get_so_error()._2)
@printf[I32]("\tgetsockopt get_tcp_nodelay = %d\n".cstring(),
conn.get_tcp_nodelay()._2)
@printf[I32]("\tgetsockopt set_tcp_nodelay(true) return value = %d\n".cstring(),
conn.set_tcp_nodelay(true))
@printf[I32]("\tgetsockopt get_tcp_nodelay = %d\n".cstring(), conn.get_tcp_nodelay()._2)

@printf[I32]("\tgetsockopt rcvbuf = %d\n".cstring(), conn.get_so_rcvbuf()._2)
@printf[I32]("\tgetsockopt sndbuf = %d\n".cstring(), conn.get_so_sndbuf()._2)
@printf[I32]("\tsetsockopt rcvbuf %d return was %d\n".cstring(), bufsiz,
conn.set_so_rcvbuf(bufsiz))
@printf[I32]("\tsetsockopt sndbuf %d return was %d\n".cstring(), bufsiz,
conn.set_so_rcvbuf(bufsiz))

fun ref throttled(connection: TCPConnection ref) =>
_out.print("Experiencing backpressure!")
Backpressure(_auth)
Expand All @@ -77,6 +96,7 @@ class SlowDown is TCPConnectionNotify
Backpressure.release(_auth)

fun ref connect_failed(conn: TCPConnection ref) =>
@printf[I32]("connect_failed\n".cstring())
None

class Send is TimerNotify
Expand All @@ -86,7 +106,7 @@ class Send is TimerNotify
_sending_actor = sending_actor

fun ref apply(timer: Timer, count: U64): Bool =>
let data = recover val Array[U8].init(72, 1024) end
let data = recover val Array[U8].init(72, 16384) end
_sending_actor.write(data)
_sending_actor.write("hi\n")
true
Expand Down
3 changes: 3 additions & 0 deletions packages/builtin/platform.pony
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ primitive Platform
fun llp64(): Bool => compile_intrinsic
fun ilp32(): Bool => compile_intrinsic

fun bigendian(): Bool => compile_intrinsic
fun littleendian(): Bool => compile_intrinsic

fun native128(): Bool => compile_intrinsic
fun debug(): Bool => compile_intrinsic
156 changes: 156 additions & 0 deletions packages/net/ossocket.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@

primitive _OSSocket
"""
Socket type-independent wrapper functions for `getsockopt(2)` and
`setsockopt(2)` system calls for internal `net` package use.
"""

fun get_so_error(fd: U32): (U32, U32) =>
"""
Wrapper for the FFI call `getsockopt(fd, SOL_SOCKET, SO_ERROR, ...)`
"""
getsockopt_u32(fd, OSSockOpt.sol_socket(), OSSockOpt.so_error())

fun get_so_rcvbuf(fd: U32): (U32, U32) =>
"""
Wrapper for the FFI call `getsockopt(fd, SOL_SOCKET, SO_RCVBUF, ...)`
"""
getsockopt_u32(fd, OSSockOpt.sol_socket(), OSSockOpt.so_rcvbuf())

fun get_so_sndbuf(fd: U32): (U32, U32) =>
"""
Wrapper for the FFI call `getsockopt(fd, SOL_SOCKET, SO_SNDBUF, ...)`
"""
getsockopt_u32(fd, OSSockOpt.sol_socket(), OSSockOpt.so_sndbuf())

fun set_so_rcvbuf(fd: U32, bufsize: U32): U32 =>
"""
Wrapper for the FFI call `setsockopt(fd, SOL_SOCKET, SO_RCVBUF, ...)`
"""
setsockopt_u32(fd, OSSockOpt.sol_socket(), OSSockOpt.so_rcvbuf(), bufsize)

fun set_so_sndbuf(fd: U32, bufsize: U32): U32 =>
"""
Wrapper for the FFI call `setsockopt(fd, SOL_SOCKET, SO_SNDBUF, ...)`
"""
setsockopt_u32(fd, OSSockOpt.sol_socket(), OSSockOpt.so_sndbuf(), bufsize)


fun getsockopt(fd: U32, level: I32, option_name: I32, option: Array[U8]): (U32, U32) =>
"""
General wrapper for sockets to the `getsockopt(2)` system call.

In case of system call success, this function returns the 2-tuple:
1. The integer `0`.
2. The value of the `*(uint32_t)option_length` argument set by
`getsockopt(2)`. The caller must use this length to properly
interpret the bytes written by the kernel into the `option`
byte array: this length may be smaller than `option`'s
original size.

In case of system call failure, this function returns the 2-tuple:
1. The value of `errno`.
2. An undefined value that must be ignored.
"""
get_so(fd, level, option_name, option)

fun getsockopt_u32(fd: U32, level: I32, option_name: I32): (U32, U32) =>
var word: Array[U8] ref = [0;0;0;0]
"""
Wrapper for sockets to the `getsockopt(2)` system call where
the kernel's returned option value is a C `uint32_t` type / Pony
type `U32`.

In case of system call success, this function returns the 2-tuple:
1. The integer `0`.
2. The `*option_value` returned by the kernel converted to a Pony `U32`.

In case of system call failure, this function returns the 2-tuple:
1. The value of `errno`.
2. An undefined value that must be ignored.
"""
(let errno: U32, _) = get_so(fd, level, option_name, word)

if errno == 0 then
try
(errno, bytes4_to_u32(word)?)
else
(1, 0)
end
else
(errno, 0)
end

fun setsockopt(fd: U32, level: I32, option_name: I32, option: Array[U8]): U32 =>
"""
General wrapper for sockets to the `setsockopt(2)` system call.

The caller is responsible for the correct size and byte contents of
the `option` array for the requested `level` and `option_name`,
including using the appropriate CPU endian byte order.

This function returns `0` on success, else the value of `errno` on
failure.
"""
set_so(fd, level, option_name, option)

fun setsockopt_u32(fd: U32, level: I32, option_name: I32, option: U32): U32 =>
"""
Wrapper for sockets to the `setsockopt(2)` system call where
the kernel expects an option value of a C `uint32_t` type / Pony
type `U32`.

This function returns `0` on success, else the value of `errno` on
failure.
"""
var word: Array[U8] ref = u32_to_bytes4(option)
set_so(fd, level, option_name, word)

fun get_so(fd: U32, level: I32, option_name: I32, option: Array[U8]): (U32, U32) =>
"""
Low-level interface to `getsockopt(2)` via `@pony_os_getsockopt[U32]()`.

In case of system call success, this function returns the 2-tuple:
1. The integer `0`.
2. The value of the `*(uint32_t)option_length` argument set by
`getsockopt(2)`.

In case of system call failure, `errno` is returned in the first
element of the 2-tuple, and the second element's value is junk.
"""
var option_size: U32 = option.size().u32()
let result: U32 = @pony_os_getsockopt[U32](fd, level, option_name,
option.cpointer(), addressof option_size)

if result == 0 then
(result, option_size)
else
(result, U32(0))
end

fun set_so(fd: U32, level: I32, option_name: I32, option: Array[U8]): U32 =>
var option_size: U32 = option.size().u32()
"""
Low-level interface to `setsockopt(2)` via `@pony_os_setsockopt[U32]()`.

This function returns `0` on success, else the value of `errno` on
failure.
"""
@pony_os_setsockopt[U32](fd, level, option_name,
option.cpointer(), option_size)

fun bytes4_to_u32(b: Array[U8]): U32 ? =>
ifdef littleendian then
(b(3)?.u32() << 24) or (b(2)?.u32() << 16) or (b(1)?.u32() << 8) or b(0)?.u32()
else
(b(0)?.u32() << 24) or (b(1)?.u32() << 16) or (b(2)?.u32() << 8) or b(3)?.u32()
end

fun u32_to_bytes4(option: U32): Array[U8] =>
ifdef littleendian then
[ (option and 0xFF).u8(); ((option >> 8) and 0xFF).u8()
((option >> 16) and 0xFF).u8(); ((option >> 24) and 0xFF).u8() ]
else
[ ((option >> 24) and 0xFF).u8(); ((option >> 16) and 0xFF).u8()
((option >> 8) and 0xFF).u8(); (option and 0xFF).u8() ]
end
Loading