Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
varnish-cache
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
varnishcache
varnish-cache
Commits
856d5627
Commit
856d5627
authored
Mar 07, 2017
by
Poul-Henning Kamp
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
The beginnings of a TX-scheduling facility.
parent
c14a8185
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
70 additions
and
12 deletions
+70
-12
cache_http2.h
bin/varnishd/http2/cache_http2.h
+11
-0
cache_http2_deliver.c
bin/varnishd/http2/cache_http2_deliver.c
+8
-0
cache_http2_proto.c
bin/varnishd/http2/cache_http2_proto.c
+8
-8
cache_http2_send.c
bin/varnishd/http2/cache_http2_send.c
+38
-2
cache_http2_session.c
bin/varnishd/http2/cache_http2_session.c
+5
-2
No files found.
bin/varnishd/http2/cache_http2.h
View file @
856d5627
...
@@ -122,6 +122,9 @@ struct h2_req {
...
@@ -122,6 +122,9 @@ struct h2_req {
VTAILQ_ENTRY
(
h2_req
)
list
;
VTAILQ_ENTRY
(
h2_req
)
list
;
int64_t
window
;
int64_t
window
;
struct
h2h_decode
*
decode
;
struct
h2h_decode
*
decode
;
struct
worker
*
tx_wrk
;
VTAILQ_ENTRY
(
h2_req
)
tx_list
;
};
};
VTAILQ_HEAD
(
h2_req_s
,
h2_req
);
VTAILQ_HEAD
(
h2_req_s
,
h2_req
);
...
@@ -158,6 +161,10 @@ struct h2_sess {
...
@@ -158,6 +161,10 @@ struct h2_sess {
struct
req
*
new_req
;
struct
req
*
new_req
;
int
go_away
;
int
go_away
;
uint32_t
go_away_last_stream
;
uint32_t
go_away_last_stream
;
VTAILQ_HEAD
(,
h2_req
)
txqueue
;
struct
h2_req
req0
[
1
];
};
};
/* http2/cache_http2_panic.c */
/* http2/cache_http2_panic.c */
...
@@ -191,6 +198,10 @@ h2_error h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d);
...
@@ -191,6 +198,10 @@ h2_error h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d);
h2_error
h2h_decode_bytes
(
struct
h2_sess
*
h2
,
struct
h2h_decode
*
d
,
h2_error
h2h_decode_bytes
(
struct
h2_sess
*
h2
,
struct
h2h_decode
*
d
,
const
uint8_t
*
ptr
,
size_t
len
);
const
uint8_t
*
ptr
,
size_t
len
);
/* cache_http2_send.c */
void
H2_Send_Get
(
struct
worker
*
,
struct
h2_sess
*
,
struct
h2_req
*
);
void
H2_Send_Rel
(
struct
worker
*
,
struct
h2_sess
*
,
struct
h2_req
*
);
h2_error
H2_Send_Frame
(
struct
worker
*
,
const
struct
h2_sess
*
,
h2_error
H2_Send_Frame
(
struct
worker
*
,
const
struct
h2_sess
*
,
h2_frame
type
,
uint8_t
flags
,
uint32_t
len
,
uint32_t
stream
,
h2_frame
type
,
uint8_t
flags
,
uint32_t
len
,
uint32_t
stream
,
const
void
*
);
const
void
*
);
...
...
bin/varnishd/http2/cache_http2_deliver.c
View file @
856d5627
...
@@ -85,8 +85,10 @@ h2_bytes(struct req *req, enum vdp_action act, void **priv,
...
@@ -85,8 +85,10 @@ h2_bytes(struct req *req, enum vdp_action act, void **priv,
return
(
0
);
return
(
0
);
AZ
(
req
->
vdp_nxt
);
/* always at the bottom of the pile */
AZ
(
req
->
vdp_nxt
);
/* always at the bottom of the pile */
H2_Send_Get
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
H2_Send
(
req
->
wrk
,
r2
,
H2_Send
(
req
->
wrk
,
r2
,
act
==
VDP_FLUSH
?
1
:
0
,
H2_F_DATA
,
H2FF_NONE
,
len
,
ptr
);
act
==
VDP_FLUSH
?
1
:
0
,
H2_F_DATA
,
H2FF_NONE
,
len
,
ptr
);
H2_Send_Rel
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
return
(
0
);
return
(
0
);
}
}
...
@@ -140,11 +142,13 @@ h2_minimal_response(struct req *req, uint16_t status)
...
@@ -140,11 +142,13 @@ h2_minimal_response(struct req *req, uint16_t status)
req
->
err_code
=
status
;
req
->
err_code
=
status
;
/* XXX return code checking once H2_Send returns anything but 0 */
/* XXX return code checking once H2_Send returns anything but 0 */
H2_Send_Get
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
H2_Send
(
req
->
wrk
,
r2
,
1
,
H2_Send
(
req
->
wrk
,
r2
,
1
,
H2_F_HEADERS
,
H2_F_HEADERS
,
H2FF_HEADERS_END_HEADERS
|
H2FF_HEADERS_END_HEADERS
|
(
status
<
200
?
0
:
H2FF_HEADERS_END_STREAM
),
(
status
<
200
?
0
:
H2FF_HEADERS_END_STREAM
),
l
,
buf
);
l
,
buf
);
H2_Send_Rel
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
return
(
0
);
return
(
0
);
}
}
...
@@ -240,8 +244,10 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody)
...
@@ -240,8 +244,10 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody)
sz
=
(
char
*
)
p
-
req
->
ws
->
f
;
sz
=
(
char
*
)
p
-
req
->
ws
->
f
;
/* XXX: Optimize !sendbody case */
/* XXX: Optimize !sendbody case */
H2_Send_Get
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
H2_Send
(
req
->
wrk
,
r2
,
1
,
H2_F_HEADERS
,
H2FF_HEADERS_END_HEADERS
,
H2_Send
(
req
->
wrk
,
r2
,
1
,
H2_F_HEADERS
,
H2FF_HEADERS_END_HEADERS
,
sz
,
req
->
ws
->
f
);
sz
,
req
->
ws
->
f
);
H2_Send_Rel
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
WS_Release
(
req
->
ws
,
0
);
WS_Release
(
req
->
ws
,
0
);
...
@@ -254,7 +260,9 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody)
...
@@ -254,7 +260,9 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody)
err
=
VDP_DeliverObj
(
req
);
err
=
VDP_DeliverObj
(
req
);
/*XXX*/
(
void
)
err
;
/*XXX*/
(
void
)
err
;
H2_Send_Get
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
H2_Send
(
req
->
wrk
,
r2
,
1
,
H2_F_DATA
,
H2FF_DATA_END_STREAM
,
0
,
NULL
);
H2_Send
(
req
->
wrk
,
r2
,
1
,
H2_F_DATA
,
H2FF_DATA_END_STREAM
,
0
,
NULL
);
H2_Send_Rel
(
req
->
wrk
,
r2
->
h2sess
,
r2
);
AZ
(
req
->
wrk
->
v1l
);
AZ
(
req
->
wrk
->
v1l
);
VDP_close
(
req
);
VDP_close
(
req
);
...
...
bin/varnishd/http2/cache_http2_proto.c
View file @
856d5627
...
@@ -186,10 +186,10 @@ h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
...
@@ -186,10 +186,10 @@ h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
return
(
H2CE_PROTOCOL_ERROR
);
return
(
H2CE_PROTOCOL_ERROR
);
if
(
h2
->
rxf_flags
!=
0
)
// We never send pings
if
(
h2
->
rxf_flags
!=
0
)
// We never send pings
return
(
H2SE_PROTOCOL_ERROR
);
return
(
H2SE_PROTOCOL_ERROR
);
Lck_Lock
(
&
h2
->
sess
->
mtx
);
H2_Send_Get
(
wrk
,
h2
,
r2
);
H2_Send_Frame
(
wrk
,
h2
,
H2_Send_Frame
(
wrk
,
h2
,
H2_F_PING
,
H2FF_PING_ACK
,
8
,
0
,
h2
->
rxf_data
);
H2_F_PING
,
H2FF_PING_ACK
,
8
,
0
,
h2
->
rxf_data
);
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
H2_Send_Rel
(
wrk
,
h2
,
r2
);
return
(
0
);
return
(
0
);
}
}
...
@@ -357,10 +357,10 @@ h2_rx_settings(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
...
@@ -357,10 +357,10 @@ h2_rx_settings(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
if
(
retval
)
if
(
retval
)
return
(
retval
);
return
(
retval
);
}
}
Lck_Lock
(
&
h2
->
sess
->
mtx
);
H2_Send_Get
(
wrk
,
h2
,
r2
);
H2_Send_Frame
(
wrk
,
h2
,
H2_Send_Frame
(
wrk
,
h2
,
H2_F_SETTINGS
,
H2FF_SETTINGS_ACK
,
0
,
0
,
NULL
);
H2_F_SETTINGS
,
H2FF_SETTINGS_ACK
,
0
,
0
,
NULL
);
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
H2_Send_Rel
(
wrk
,
h2
,
r2
);
}
}
return
(
0
);
return
(
0
);
}
}
...
@@ -636,10 +636,10 @@ h2_procframe(struct worker *wrk, struct h2_sess *h2,
...
@@ -636,10 +636,10 @@ h2_procframe(struct worker *wrk, struct h2_sess *h2,
VSLb
(
h2
->
vsl
,
SLT_Debug
,
"H2: stream %u: %s"
,
h2
->
rxf_stream
,
h2e
->
txt
);
VSLb
(
h2
->
vsl
,
SLT_Debug
,
"H2: stream %u: %s"
,
h2
->
rxf_stream
,
h2e
->
txt
);
vbe32enc
(
b
,
h2e
->
val
);
vbe32enc
(
b
,
h2e
->
val
);
Lck_Lock
(
&
h2
->
sess
->
mtx
);
H2_Send_Get
(
wrk
,
h2
,
r2
);
(
void
)
H2_Send_Frame
(
wrk
,
h2
,
H2_F_RST_STREAM
,
(
void
)
H2_Send_Frame
(
wrk
,
h2
,
H2_F_RST_STREAM
,
0
,
sizeof
b
,
h2
->
rxf_stream
,
b
);
0
,
sizeof
b
,
h2
->
rxf_stream
,
b
);
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
H2_Send_Rel
(
wrk
,
h2
,
r2
);
h2_del_req
(
wrk
,
r2
);
h2_del_req
(
wrk
,
r2
);
return
(
0
);
return
(
0
);
...
@@ -732,9 +732,9 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
...
@@ -732,9 +732,9 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
if
(
h2e
)
{
if
(
h2e
)
{
vbe32enc
(
b
,
h2
->
highest_stream
);
vbe32enc
(
b
,
h2
->
highest_stream
);
vbe32enc
(
b
+
4
,
h2e
->
val
);
vbe32enc
(
b
+
4
,
h2e
->
val
);
Lck_Lock
(
&
h2
->
sess
->
mtx
);
H2_Send_Get
(
wrk
,
h2
,
h2
->
req0
);
(
void
)
H2_Send_Frame
(
wrk
,
h2
,
H2_F_GOAWAY
,
0
,
8
,
0
,
b
);
(
void
)
H2_Send_Frame
(
wrk
,
h2
,
H2_F_GOAWAY
,
0
,
8
,
0
,
b
);
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
H2_Send_Rel
(
wrk
,
h2
,
h2
->
req0
);
}
}
return
(
h2e
?
0
:
1
);
return
(
h2e
?
0
:
1
);
}
}
bin/varnishd/http2/cache_http2_send.c
View file @
856d5627
...
@@ -36,6 +36,36 @@
...
@@ -36,6 +36,36 @@
#include "vend.h"
#include "vend.h"
void
H2_Send_Get
(
struct
worker
*
wrk
,
struct
h2_sess
*
h2
,
struct
h2_req
*
r2
)
{
CHECK_OBJ_NOTNULL
(
wrk
,
WORKER_MAGIC
);
CHECK_OBJ_NOTNULL
(
h2
,
H2_SESS_MAGIC
);
CHECK_OBJ_NOTNULL
(
r2
,
H2_REQ_MAGIC
);
r2
->
tx_wrk
=
wrk
;
Lck_Lock
(
&
h2
->
sess
->
mtx
);
VTAILQ_INSERT_TAIL
(
&
h2
->
txqueue
,
r2
,
tx_list
);
while
(
VTAILQ_FIRST
(
&
h2
->
txqueue
)
!=
r2
)
Lck_CondWait
(
&
wrk
->
cond
,
&
h2
->
sess
->
mtx
,
0
);
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
}
void
H2_Send_Rel
(
struct
worker
*
wrk
,
struct
h2_sess
*
h2
,
struct
h2_req
*
r2
)
{
CHECK_OBJ_NOTNULL
(
wrk
,
WORKER_MAGIC
);
CHECK_OBJ_NOTNULL
(
h2
,
H2_SESS_MAGIC
);
CHECK_OBJ_NOTNULL
(
r2
,
H2_REQ_MAGIC
);
Lck_Lock
(
&
h2
->
sess
->
mtx
);
assert
(
VTAILQ_FIRST
(
&
h2
->
txqueue
)
==
r2
);
VTAILQ_REMOVE
(
&
h2
->
txqueue
,
r2
,
tx_list
);
r2
=
VTAILQ_FIRST
(
&
h2
->
txqueue
);
if
(
r2
!=
NULL
)
AZ
(
pthread_cond_signal
(
&
r2
->
tx_wrk
->
cond
));
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
}
static
void
static
void
h2_mk_hdr
(
uint8_t
*
hdr
,
h2_frame
ftyp
,
uint8_t
flags
,
h2_mk_hdr
(
uint8_t
*
hdr
,
h2_frame
ftyp
,
uint8_t
flags
,
uint32_t
len
,
uint32_t
stream
)
uint32_t
len
,
uint32_t
stream
)
...
@@ -64,7 +94,6 @@ H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
...
@@ -64,7 +94,6 @@ H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
ssize_t
s
;
ssize_t
s
;
(
void
)
wrk
;
(
void
)
wrk
;
Lck_AssertHeld
(
&
h2
->
sess
->
mtx
);
AN
(
ftyp
);
AN
(
ftyp
);
AZ
(
flags
&
~
(
ftyp
->
flags
));
AZ
(
flags
&
~
(
ftyp
->
flags
));
...
@@ -74,7 +103,9 @@ H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
...
@@ -74,7 +103,9 @@ H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
AZ
(
ftyp
->
act_snonzero
);
AZ
(
ftyp
->
act_snonzero
);
h2_mk_hdr
(
hdr
,
ftyp
,
flags
,
len
,
stream
);
h2_mk_hdr
(
hdr
,
ftyp
,
flags
,
len
,
stream
);
Lck_Lock
(
&
h2
->
sess
->
mtx
);
VSLb_bin
(
h2
->
vsl
,
SLT_H2TxHdr
,
9
,
hdr
);
VSLb_bin
(
h2
->
vsl
,
SLT_H2TxHdr
,
9
,
hdr
);
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
s
=
write
(
h2
->
sess
->
fd
,
hdr
,
sizeof
hdr
);
s
=
write
(
h2
->
sess
->
fd
,
hdr
,
sizeof
hdr
);
if
(
s
!=
sizeof
hdr
)
if
(
s
!=
sizeof
hdr
)
...
@@ -83,7 +114,9 @@ H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
...
@@ -83,7 +114,9 @@ H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
s
=
write
(
h2
->
sess
->
fd
,
ptr
,
len
);
s
=
write
(
h2
->
sess
->
fd
,
ptr
,
len
);
if
(
s
!=
len
)
if
(
s
!=
len
)
return
(
H2CE_PROTOCOL_ERROR
);
// XXX Need private ?
return
(
H2CE_PROTOCOL_ERROR
);
// XXX Need private ?
Lck_Lock
(
&
h2
->
sess
->
mtx
);
VSLb_bin
(
h2
->
vsl
,
SLT_H2TxBody
,
len
,
ptr
);
VSLb_bin
(
h2
->
vsl
,
SLT_H2TxBody
,
len
,
ptr
);
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
}
}
return
(
0
);
return
(
0
);
}
}
...
@@ -106,11 +139,14 @@ H2_Send(struct worker *wrk, struct h2_req *r2, int flush,
...
@@ -106,11 +139,14 @@ H2_Send(struct worker *wrk, struct h2_req *r2, int flush,
(
void
)
flush
;
(
void
)
flush
;
CHECK_OBJ_NOTNULL
(
wrk
,
WORKER_MAGIC
);
CHECK_OBJ_NOTNULL
(
r2
,
H2_REQ_MAGIC
);
CHECK_OBJ_NOTNULL
(
r2
,
H2_REQ_MAGIC
);
h2
=
r2
->
h2sess
;
h2
=
r2
->
h2sess
;
CHECK_OBJ_NOTNULL
(
h2
,
H2_SESS_MAGIC
);
CHECK_OBJ_NOTNULL
(
h2
,
H2_SESS_MAGIC
);
assert
(
len
==
0
||
ptr
!=
NULL
);
assert
(
len
==
0
||
ptr
!=
NULL
);
assert
(
VTAILQ_FIRST
(
&
h2
->
txqueue
)
==
r2
);
AN
(
ftyp
);
AN
(
ftyp
);
AZ
(
flags
&
~
(
ftyp
->
flags
));
AZ
(
flags
&
~
(
ftyp
->
flags
));
if
(
r2
->
stream
==
0
)
if
(
r2
->
stream
==
0
)
...
@@ -120,6 +156,7 @@ H2_Send(struct worker *wrk, struct h2_req *r2, int flush,
...
@@ -120,6 +156,7 @@ H2_Send(struct worker *wrk, struct h2_req *r2, int flush,
Lck_Lock
(
&
h2
->
sess
->
mtx
);
Lck_Lock
(
&
h2
->
sess
->
mtx
);
mfs
=
h2
->
remote_settings
.
max_frame_size
;
mfs
=
h2
->
remote_settings
.
max_frame_size
;
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
if
(
len
<
mfs
)
{
if
(
len
<
mfs
)
{
retval
=
H2_Send_Frame
(
wrk
,
h2
,
retval
=
H2_Send_Frame
(
wrk
,
h2
,
ftyp
,
flags
,
len
,
r2
->
stream
,
ptr
);
ftyp
,
flags
,
len
,
r2
->
stream
,
ptr
);
...
@@ -145,6 +182,5 @@ H2_Send(struct worker *wrk, struct h2_req *r2, int flush,
...
@@ -145,6 +182,5 @@ H2_Send(struct worker *wrk, struct h2_req *r2, int flush,
ftyp
=
ftyp
->
continuation
;
ftyp
=
ftyp
->
continuation
;
}
while
(
len
>
0
&&
retval
==
0
);
}
while
(
len
>
0
&&
retval
==
0
);
}
}
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
return
(
retval
);
return
(
retval
);
}
}
bin/varnishd/http2/cache_http2_session.c
View file @
856d5627
...
@@ -99,6 +99,7 @@ h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
...
@@ -99,6 +99,7 @@ h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
h2
->
htc
->
rfd
=
&
sp
->
fd
;
h2
->
htc
->
rfd
=
&
sp
->
fd
;
h2
->
sess
=
sp
;
h2
->
sess
=
sp
;
VTAILQ_INIT
(
&
h2
->
streams
);
VTAILQ_INIT
(
&
h2
->
streams
);
VTAILQ_INIT
(
&
h2
->
txqueue
);
h2
->
local_settings
=
H2_proto_settings
;
h2
->
local_settings
=
H2_proto_settings
;
h2
->
remote_settings
=
H2_proto_settings
;
h2
->
remote_settings
=
H2_proto_settings
;
...
@@ -108,6 +109,8 @@ h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
...
@@ -108,6 +109,8 @@ h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
SES_Reserve_xport_priv
(
sp
,
&
up
);
SES_Reserve_xport_priv
(
sp
,
&
up
);
*
up
=
(
uintptr_t
)
h2
;
*
up
=
(
uintptr_t
)
h2
;
INIT_OBJ
(
h2
->
req0
,
H2_REQ_MAGIC
);
h2
->
req0
->
h2sess
=
h2
;
}
}
AN
(
up
);
AN
(
up
);
CAST_OBJ_NOTNULL
(
h2
,
(
void
*
)(
*
up
),
H2_SESS_MAGIC
);
CAST_OBJ_NOTNULL
(
h2
,
(
void
*
)(
*
up
),
H2_SESS_MAGIC
);
...
@@ -333,13 +336,13 @@ h2_new_session(struct worker *wrk, void *arg)
...
@@ -333,13 +336,13 @@ h2_new_session(struct worker *wrk, void *arg)
THR_SetRequest
(
h2
->
srq
);
THR_SetRequest
(
h2
->
srq
);
Lck_Lock
(
&
h2
->
sess
->
mtx
);
H2_Send_Get
(
wrk
,
h2
,
h2
->
req0
);
H2_Send_Frame
(
wrk
,
h2
,
H2_Send_Frame
(
wrk
,
h2
,
H2_F_SETTINGS
,
H2FF_NONE
,
sizeof
H2_settings
,
0
,
H2_settings
);
H2_F_SETTINGS
,
H2FF_NONE
,
sizeof
H2_settings
,
0
,
H2_settings
);
H2_Send_Rel
(
wrk
,
h2
,
h2
->
req0
);
/* and off we go... */
/* and off we go... */
h2
->
cond
=
&
wrk
->
cond
;
h2
->
cond
=
&
wrk
->
cond
;
Lck_Unlock
(
&
h2
->
sess
->
mtx
);
while
(
h2_rxframe
(
wrk
,
h2
))
{
while
(
h2_rxframe
(
wrk
,
h2
))
{
WS_Reset
(
h2
->
ws
,
wsp
);
WS_Reset
(
h2
->
ws
,
wsp
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment