Skip to content

Commit 056991b

Browse files
author
Gijs Peskens
authored
Full errno handling (#35)
Handle all SRT_E values and create typed errors that can be checked by consumers of srtgo library. Based on the runtime syscall.Errno code. In all functions that can return an srt error value the OS thread is locked, to make sure golang does not schedule is away from the thread. This is needed since srt stores it's error value in thread local storage.
1 parent 2161119 commit 056991b

File tree

3 files changed

+248
-53
lines changed

3 files changed

+248
-53
lines changed

errors.go

+197
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
package srtgo
22

3+
/*
4+
#cgo LDFLAGS: -lsrt
5+
#include <srt/srt.h>
6+
*/
7+
import "C"
8+
import (
9+
"strconv"
10+
"syscall"
11+
)
12+
313
type SrtInvalidSock struct{}
414
type SrtRendezvousUnbound struct{}
515
type SrtSockConnected struct{}
@@ -43,3 +53,190 @@ func (m *SrtEpollTimeout) Timeout() bool {
4353
func (m *SrtEpollTimeout) Temporary() bool {
4454
return true
4555
}
56+
57+
//MUST be called from same OS thread that generated the error (i.e.: use runtime.LockOSThread())
58+
func srtGetAndClearError() error {
59+
defer C.srt_clearlasterror()
60+
eSysErrno := C.int(0)
61+
errno := C.srt_getlasterror(&eSysErrno)
62+
srterr := SRTErrno(errno)
63+
if eSysErrno != 0 {
64+
return srterr.wrapSysErr(syscall.Errno(eSysErrno))
65+
}
66+
return srterr
67+
}
68+
69+
//Based of off golang errno handling: https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/syscall/syscall_unix.go;l=114
70+
type SRTErrno int
71+
72+
func (e SRTErrno) Error() string {
73+
//Workaround for unknown being -1
74+
if e == Unknown {
75+
return "Internal error when setting the right error code"
76+
}
77+
if 0 <= int(e) && int(e) < len(srterrors) {
78+
s := srterrors[e]
79+
if s != "" {
80+
return s
81+
}
82+
}
83+
return "srterrno: " + strconv.Itoa(int(e))
84+
}
85+
86+
func (e SRTErrno) Is(target error) bool {
87+
//for backwards compat
88+
switch target.(type) {
89+
case *SrtInvalidSock:
90+
return e == EInvSock
91+
case *SrtRendezvousUnbound:
92+
return e == ERdvUnbound
93+
case *SrtSockConnected:
94+
return e == EConnSock
95+
case *SrtConnectionRejected:
96+
return e == EConnRej
97+
case *SrtConnectTimeout:
98+
return e == ETimeout
99+
case *SrtSocketClosed:
100+
return e == ESClosed
101+
}
102+
return false
103+
}
104+
105+
func (e SRTErrno) Temporary() bool {
106+
return e == EAsyncFAIL || e == EAsyncRCV || e == EAsyncSND || e == ECongest || e == ETimeout
107+
}
108+
109+
func (e SRTErrno) Timeout() bool {
110+
return e == ETimeout
111+
}
112+
113+
func (e SRTErrno) wrapSysErr(errno syscall.Errno) error {
114+
return &srtErrnoSysErrnoWrapped{
115+
e: e,
116+
eSys: errno,
117+
}
118+
}
119+
120+
type srtErrnoSysErrnoWrapped struct {
121+
e SRTErrno
122+
eSys syscall.Errno
123+
}
124+
125+
func (e *srtErrnoSysErrnoWrapped) Error() string {
126+
return e.e.Error()
127+
}
128+
129+
func (e *srtErrnoSysErrnoWrapped) Is(target error) bool {
130+
return e.e.Is(target)
131+
}
132+
133+
func (e *srtErrnoSysErrnoWrapped) Temporary() bool {
134+
return e.e.Temporary()
135+
}
136+
137+
func (e *srtErrnoSysErrnoWrapped) Timeout() bool {
138+
return e.e.Timeout()
139+
}
140+
141+
func (e *srtErrnoSysErrnoWrapped) Unwrap() error {
142+
return error(e.eSys)
143+
}
144+
145+
//Shadows SRT_ERRNO srtcore/srt.h line 490+
146+
const (
147+
Unknown = SRTErrno(C.SRT_EUNKNOWN)
148+
Success = SRTErrno(C.SRT_SUCCESS)
149+
//Major: SETUP
150+
EConnSetup = SRTErrno(C.SRT_ECONNSETUP)
151+
ENoServer = SRTErrno(C.SRT_ENOSERVER)
152+
EConnRej = SRTErrno(C.SRT_ECONNREJ)
153+
ESockFail = SRTErrno(C.SRT_ESOCKFAIL)
154+
ESecFail = SRTErrno(C.SRT_ESECFAIL)
155+
ESClosed = SRTErrno(C.SRT_ESCLOSED)
156+
//Major: CONNECTION
157+
EConnFail = SRTErrno(C.SRT_ECONNFAIL)
158+
EConnLost = SRTErrno(C.SRT_ECONNLOST)
159+
ENoConn = SRTErrno(C.SRT_ENOCONN)
160+
//Major: SYSTEMRES
161+
EResource = SRTErrno(C.SRT_ERESOURCE)
162+
EThread = SRTErrno(C.SRT_ETHREAD)
163+
EnoBuf = SRTErrno(C.SRT_ENOBUF)
164+
ESysObj = SRTErrno(C.SRT_ESYSOBJ)
165+
//Major: FILESYSTEM
166+
EFile = SRTErrno(C.SRT_EFILE)
167+
EInvRdOff = SRTErrno(C.SRT_EINVRDOFF)
168+
ERdPerm = SRTErrno(C.SRT_ERDPERM)
169+
EInvWrOff = SRTErrno(C.SRT_EINVWROFF)
170+
EWrPerm = SRTErrno(C.SRT_EWRPERM)
171+
//Major: NOTSUP
172+
EInvOp = SRTErrno(C.SRT_EINVOP)
173+
EBoundSock = SRTErrno(C.SRT_EBOUNDSOCK)
174+
EConnSock = SRTErrno(C.SRT_ECONNSOCK)
175+
EInvParam = SRTErrno(C.SRT_EINVPARAM)
176+
EInvSock = SRTErrno(C.SRT_EINVSOCK)
177+
EUnboundSock = SRTErrno(C.SRT_EUNBOUNDSOCK)
178+
ENoListen = SRTErrno(C.SRT_ENOLISTEN)
179+
ERdvNoServ = SRTErrno(C.SRT_ERDVNOSERV)
180+
ERdvUnbound = SRTErrno(C.SRT_ERDVUNBOUND)
181+
EInvalMsgAPI = SRTErrno(C.SRT_EINVALMSGAPI)
182+
EInvalBufferAPI = SRTErrno(C.SRT_EINVALBUFFERAPI)
183+
EDupListen = SRTErrno(C.SRT_EDUPLISTEN)
184+
ELargeMsg = SRTErrno(C.SRT_ELARGEMSG)
185+
EInvPollID = SRTErrno(C.SRT_EINVPOLLID)
186+
EPollEmpty = SRTErrno(C.SRT_EPOLLEMPTY)
187+
//EBindConflict = SRTErrno(C.SRT_EBINDCONFLICT)
188+
//Major: AGAIN
189+
EAsyncFAIL = SRTErrno(C.SRT_EASYNCFAIL)
190+
EAsyncSND = SRTErrno(C.SRT_EASYNCSND)
191+
EAsyncRCV = SRTErrno(C.SRT_EASYNCRCV)
192+
ETimeout = SRTErrno(C.SRT_ETIMEOUT)
193+
ECongest = SRTErrno(C.SRT_ECONGEST)
194+
//Major: PEERERROR
195+
EPeer = SRTErrno(C.SRT_EPEERERR)
196+
)
197+
198+
//Unknown cannot be here since it would have a negative index!
199+
//Error strings taken from: https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md
200+
var srterrors = [...]string{
201+
Success: "The value set when the last error was cleared and no error has occurred since then",
202+
EConnSetup: "General setup error resulting from internal system state",
203+
ENoServer: "Connection timed out while attempting to connect to the remote address",
204+
EConnRej: "Connection has been rejected",
205+
ESockFail: "An error occurred when trying to call a system function on an internally used UDP socket",
206+
ESecFail: "A possible tampering with the handshake packets was detected, or encryption request wasn't properly fulfilled.",
207+
ESClosed: "A socket that was vital for an operation called in blocking mode has been closed during the operation",
208+
EConnFail: "General connection failure of unknown details",
209+
EConnLost: "The socket was properly connected, but the connection has been broken",
210+
ENoConn: "The socket is not connected",
211+
EResource: "System or standard library error reported unexpectedly for unknown purpose",
212+
EThread: "System was unable to spawn a new thread when requried",
213+
EnoBuf: "System was unable to allocate memory for buffers",
214+
ESysObj: "System was unable to allocate system specific objects",
215+
EFile: "General filesystem error (for functions operating with file transmission)",
216+
EInvRdOff: "Failure when trying to read from a given position in the file",
217+
ERdPerm: "Read permission was denied when trying to read from file",
218+
EInvWrOff: "Failed to set position in the written file",
219+
EWrPerm: "Write permission was denied when trying to write to a file",
220+
EInvOp: "Invalid operation performed for the current state of a socket",
221+
EBoundSock: "The socket is currently bound and the required operation cannot be performed in this state",
222+
EConnSock: "The socket is currently connected and therefore performing the required operation is not possible",
223+
EInvParam: "Call parameters for API functions have some requirements that were not satisfied",
224+
EInvSock: "The API function required an ID of an entity (socket or group) and it was invalid",
225+
EUnboundSock: "The operation to be performed on a socket requires that it first be explicitly bound",
226+
ENoListen: "The socket passed for the operation is required to be in the listen state",
227+
ERdvNoServ: "The required operation cannot be performed when the socket is set to rendezvous mode",
228+
ERdvUnbound: "An attempt was made to connect to a socket set to rendezvous mode that was not first bound",
229+
EInvalMsgAPI: "The function was used incorrectly in the message API",
230+
EInvalBufferAPI: "The function was used incorrectly in the stream (buffer) API",
231+
EDupListen: "The port tried to be bound for listening is already busy",
232+
ELargeMsg: "Size exceeded",
233+
EInvPollID: "The epoll ID passed to an epoll function is invalid",
234+
EPollEmpty: "The epoll container currently has no subscribed sockets",
235+
//EBindConflict: "SRT_EBINDCONFLICT",
236+
EAsyncFAIL: "General asynchronous failure (not in use currently)",
237+
EAsyncSND: "Sending operation is not ready to perform",
238+
EAsyncRCV: "Receiving operation is not ready to perform",
239+
ETimeout: "The operation timed out",
240+
ECongest: "With SRTO_TSBPDMODE and SRTO_TLPKTDROP set to true, some packets were dropped by sender",
241+
EPeer: "Receiver peer is writing to a file that the agent is sending",
242+
}

0 commit comments

Comments
 (0)